Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Doc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ venv:
fi

.PHONY: dist-no-html
dist-no-html: dist-text dist-pdf dist-epub dist-texinfo
dist-no-html: dist-text dist-epub dist-texinfo

.PHONY: dist
dist:
Expand Down
20 changes: 13 additions & 7 deletions Doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -598,11 +598,17 @@
# Options for sphinxext-opengraph
# -------------------------------

ogp_site_url = 'https://docs.python.org/3/'
ogp_canonical_url = 'https://docs.python.org/3/'
ogp_site_name = 'Python documentation'
ogp_image = '_static/og-image.png'
ogp_custom_meta_tags = [
'<meta property="og:image:width" content="200">',
'<meta property="og:image:height" content="200">',
'<meta name="theme-color" content="#3776ab">',
]
ogp_social_cards = { # Used when matplotlib is installed
'image': '_static/og-image.png',
'line_color': '#3776ab',
}
ogp_custom_meta_tags = ('<meta name="theme-color" content="#3776ab">',)
if 'create-social-cards' not in tags: # noqa: F821
# Define a static preview image when not creating social cards
ogp_image = '_static/og-image.png'
ogp_custom_meta_tags += (
'<meta property="og:image:width" content="200">',
'<meta property="og:image:height" content="200">',
)
33 changes: 20 additions & 13 deletions Doc/library/html.parser.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,18 @@
This module defines a class :class:`HTMLParser` which serves as the basis for
parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML.

.. class:: HTMLParser(*, convert_charrefs=True)
.. class:: HTMLParser(*, convert_charrefs=True, scripting=False)

Create a parser instance able to parse invalid markup.

If *convert_charrefs* is ``True`` (the default), all character
references (except the ones in ``script``/``style`` elements) are
If *convert_charrefs* is true (the default), all character
references (except the ones in elements like ``script`` and ``style``) are
automatically converted to the corresponding Unicode characters.

If *scripting* is false (the default), the content of the ``noscript``
element is parsed normally; if it's true, it's returned as is without
being parsed.

An :class:`.HTMLParser` instance is fed HTML data and calls handler methods
when start tags, end tags, text, comments, and other markup elements are
encountered. The user should subclass :class:`.HTMLParser` and override its
Expand All @@ -37,6 +41,9 @@ parsing text files formatted in HTML (HyperText Mark-up Language) and XHTML.
.. versionchanged:: 3.5
The default value for argument *convert_charrefs* is now ``True``.

.. versionchanged:: 3.12.13
Added the *scripting* parameter.


Example HTML Parser Application
-------------------------------
Expand Down Expand Up @@ -159,24 +166,24 @@ implementations do nothing (except for :meth:`~HTMLParser.handle_startendtag`):
.. method:: HTMLParser.handle_data(data)

This method is called to process arbitrary data (e.g. text nodes and the
content of ``<script>...</script>`` and ``<style>...</style>``).
content of elements like ``script`` and ``style``).


.. method:: HTMLParser.handle_entityref(name)

This method is called to process a named character reference of the form
``&name;`` (e.g. ``&gt;``), where *name* is a general entity reference
(e.g. ``'gt'``). This method is never called if *convert_charrefs* is
``True``.
(e.g. ``'gt'``).
This method is only called if *convert_charrefs* is false.


.. method:: HTMLParser.handle_charref(name)

This method is called to process decimal and hexadecimal numeric character
references of the form :samp:`&#{NNN};` and :samp:`&#x{NNN};`. For example, the decimal
equivalent for ``&gt;`` is ``&#62;``, whereas the hexadecimal is ``&#x3E;``;
in this case the method will receive ``'62'`` or ``'x3E'``. This method
is never called if *convert_charrefs* is ``True``.
in this case the method will receive ``'62'`` or ``'x3E'``.
This method is only called if *convert_charrefs* is false.


.. method:: HTMLParser.handle_comment(data)
Expand Down Expand Up @@ -284,8 +291,8 @@ Parsing an element with a few attributes and a title::
Data : Python
End tag : h1

The content of ``script`` and ``style`` elements is returned as is, without
further parsing::
The content of elements like ``script`` and ``style`` is returned as is,
without further parsing::

>>> parser.feed('<style type="text/css">#python { color: green }</style>')
Start tag: style
Expand All @@ -294,10 +301,10 @@ further parsing::
End tag : style

>>> parser.feed('<script type="text/javascript">'
... 'alert("<strong>hello!</strong>");</script>')
... 'alert("<strong>hello! &#9786;</strong>");</script>')
Start tag: script
attr: ('type', 'text/javascript')
Data : alert("<strong>hello!</strong>");
Data : alert("<strong>hello! &#9786;</strong>");
End tag : script

Parsing comments::
Expand All @@ -317,7 +324,7 @@ correct char (note: these 3 references are all equivalent to ``'>'``)::

Feeding incomplete chunks to :meth:`~HTMLParser.feed` works, but
:meth:`~HTMLParser.handle_data` might be called more than once
(unless *convert_charrefs* is set to ``True``)::
if *convert_charrefs* is false::

>>> for chunk in ['<sp', 'an>buff', 'ered ', 'text</s', 'pan>']:
... parser.feed(chunk)
Expand Down
4 changes: 2 additions & 2 deletions Doc/library/http.cookies.rst
Original file line number Diff line number Diff line change
Expand Up @@ -272,9 +272,9 @@ The following example demonstrates how to use the :mod:`http.cookies` module.
Set-Cookie: chips=ahoy
Set-Cookie: vienna=finger
>>> C = cookies.SimpleCookie()
>>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";')
>>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=;";')
>>> print(C)
Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;"
Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=;"
>>> C = cookies.SimpleCookie()
>>> C["oreo"] = "doublestuff"
>>> C["oreo"]["path"] = "/"
Expand Down
12 changes: 7 additions & 5 deletions Doc/library/os.path.rst
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,17 @@ the :mod:`glob` module.)

.. function:: commonprefix(list)

Return the longest path prefix (taken character-by-character) that is a
prefix of all paths in *list*. If *list* is empty, return the empty string
Return the longest string prefix (taken character-by-character) that is a
prefix of all strings in *list*. If *list* is empty, return the empty string
(``''``).

.. note::
.. warning::

This function may return invalid paths because it works a
character at a time. To obtain a valid path, see
:func:`commonpath`.
character at a time.
If you need a **common path prefix**, then the algorithm
implemented in this function is not secure. Use
:func:`commonpath` for finding a common path prefix.

::

Expand Down
57 changes: 57 additions & 0 deletions Doc/library/pyexpat.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ The :mod:`xml.parsers.expat` module contains two functions:
*encoding* [1]_ is given it will override the implicit or explicit encoding of the
document.

.. _xmlparser-non-root:

Parsers created through :func:`!ParserCreate` are called "root" parsers,
in the sense that they do not have any parent parser attached. Non-root
parsers are created by :meth:`parser.ExternalEntityParserCreate
<xmlparser.ExternalEntityParserCreate>`.

Expat can optionally do XML namespace processing for you, enabled by providing a
value for *namespace_separator*. The value must be a one-character string; a
:exc:`ValueError` will be raised if the string has an illegal length (``None``
Expand Down Expand Up @@ -232,6 +239,55 @@ XMLParser Objects
.. versionadded:: 3.12.3


:class:`!xmlparser` objects have the following methods to mitigate some
common XML vulnerabilities.

.. method:: xmlparser.SetAllocTrackerActivationThreshold(threshold, /)

Sets the number of allocated bytes of dynamic memory needed to activate
protection against disproportionate use of RAM.

By default, parser objects have an allocation activation threshold of 64 MiB,
or equivalently 67,108,864 bytes.

An :exc:`ExpatError` is raised if this method is called on a
|xml-non-root-parser| parser.
The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset`
should not be used as they may have no special meaning.

.. versionadded:: 3.12.13

.. method:: xmlparser.SetAllocTrackerMaximumAmplification(max_factor, /)

Sets the maximum amplification factor between direct input and bytes
of dynamic memory allocated.

The amplification factor is calculated as ``allocated / direct``
while parsing, where ``direct`` is the number of bytes read from
the primary document in parsing and ``allocated`` is the number
of bytes of dynamic memory allocated in the parser hierarchy.

The *max_factor* value must be a non-NaN :class:`float` value greater than
or equal to 1.0. Amplification factors greater than 100.0 can be observed
near the start of parsing even with benign files in practice. In particular,
the activation threshold should be carefully chosen to avoid false positives.

By default, parser objects have a maximum amplification factor of 100.0.

An :exc:`ExpatError` is raised if this method is called on a
|xml-non-root-parser| parser or if *max_factor* is outside the valid range.
The corresponding :attr:`~ExpatError.lineno` and :attr:`~ExpatError.offset`
should not be used as they may have no special meaning.

.. note::

The maximum amplification factor is only considered if the threshold
that can be adjusted by :meth:`.SetAllocTrackerActivationThreshold`
is exceeded.

.. versionadded:: 3.12.13


:class:`xmlparser` objects have the following attributes:


Expand Down Expand Up @@ -948,3 +1004,4 @@ The ``errors`` module has the following attributes:
not. See https://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDecl
and https://www.iana.org/assignments/character-sets/character-sets.xhtml.

.. |xml-non-root-parser| replace:: :ref:`non-root <xmlparser-non-root>`
5 changes: 5 additions & 0 deletions Doc/library/wsgiref.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@

--------------

.. warning::

:mod:`wsgiref` is a reference implementation and is not recommended for
production. The module only implements basic security checks.

The Web Server Gateway Interface (WSGI) is a standard interface between web
server software and web applications written in Python. Having a standard
interface makes it easy to use an application that supports WSGI with a number
Expand Down
2 changes: 1 addition & 1 deletion Doc/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ sphinx~=8.2.0

blurb

sphinxext-opengraph~=0.9.0
sphinxext-opengraph~=0.13.0
sphinx-notfound-page~=1.0.0

# The theme used by the documentation is stored separately, so we need
Expand Down
15 changes: 10 additions & 5 deletions Doc/tools/templates/download.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@ <h1>{% trans %}Download Python {{ dl_version }} Documentation{% endtrans %}</h1>
<th>{% trans %}Packed as .zip{% endtrans %}</th>
<th>{% trans %}Packed as .tar.bz2{% endtrans %}</th>
</tr>
<tr>
<td>{% trans %}PDF{% endtrans %}</td>
<td>{% trans download_size="17" %}<a href="{{ dl_base }}/python-{{ dl_version }}-docs-pdf-a4.zip">Download</a> (ca. {{ download_size }} MiB){% endtrans %}</td>
<td>{% trans download_size="17" %}<a href="{{ dl_base }}/python-{{ dl_version }}-docs-pdf-a4.tar.bz2">Download</a> (ca. {{ download_size }} MiB){% endtrans %}</td>
</tr>
<tr>
<td>{% trans %}HTML{% endtrans %}</td>
<td>{% trans download_size="13" %}<a href="{{ dl_base }}/python-{{ dl_version }}-docs-html.zip">Download</a> (ca. {{ download_size }} MiB){% endtrans %}</td>
Expand All @@ -69,6 +64,16 @@ <h1>{% trans %}Download Python {{ dl_version }} Documentation{% endtrans %}</h1>

<p>{% trans %}These archives contain all the content in the documentation.{% endtrans %}</p>

<p>{% trans %}
We no longer provide updates to the pre-built PDFs of the documentation.
The previously-built archives are still available and may be of use:
<a href="{{ dl_base }}/python-{{ dl_version }}-docs-pdf-a4.zip">PDF (.zip archive)</a>;
<a href="{{ dl_base }}/python-{{ dl_version }}-docs-pdf-a4.tar.bz2">PDF (.tar.bz2 archive)</a>.
To build a PDF archive, follow the instructions in the
<a href="https://devguide.python.org/documentation/start-documenting/#building-the-documentation">Developer's Guide</a>
and run <code>make dist-pdf</code> in the <code>Doc/</code> directory of a copy of the CPython repository.
{% endtrans %}</p>


<h2>{% trans %}Unpacking{% endtrans %}</h2>

Expand Down
4 changes: 2 additions & 2 deletions Include/patchlevel.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
/*--start constants--*/
#define PY_MAJOR_VERSION 3
#define PY_MINOR_VERSION 12
#define PY_MICRO_VERSION 11
#define PY_MICRO_VERSION 13
#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL
#define PY_RELEASE_SERIAL 0

/* Version as a string */
#define PY_VERSION "3.12.11"
#define PY_VERSION "3.12.13"
/*--end constants--*/

/* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
Expand Down
5 changes: 5 additions & 0 deletions Include/pyexpat.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ struct PyExpat_CAPI
int (*SetHashSalt)(XML_Parser parser, unsigned long hash_salt);
/* might be NULL for expat < 2.6.0 */
XML_Bool (*SetReparseDeferralEnabled)(XML_Parser parser, XML_Bool enabled);
/* might be NULL for expat < 2.7.2 */
XML_Bool (*SetAllocTrackerActivationThreshold)(
XML_Parser parser, unsigned long long activationThresholdBytes);
XML_Bool (*SetAllocTrackerMaximumAmplification)(
XML_Parser parser, float maxAmplificationFactor);
/* always add new stuff to the end! */
};

15 changes: 14 additions & 1 deletion Lib/email/_header_value_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,12 @@ def make_quoted_pairs(value):
return str(value).replace('\\', '\\\\').replace('"', '\\"')


def make_parenthesis_pairs(value):
"""Escape parenthesis and backslash for use within a comment."""
return str(value).replace('\\', '\\\\') \
.replace('(', '\\(').replace(')', '\\)')


def quote_string(value):
escaped = make_quoted_pairs(value)
return f'"{escaped}"'
Expand Down Expand Up @@ -933,7 +939,7 @@ def value(self):
return ' '

def startswith_fws(self):
return True
return self and self[0] in WSP


class ValueTerminal(Terminal):
Expand Down Expand Up @@ -2922,6 +2928,13 @@ def _refold_parse_tree(parse_tree, *, policy):
[ValueTerminal(make_quoted_pairs(p), 'ptext')
for p in newparts] +
[ValueTerminal('"', 'ptext')])
if part.token_type == 'comment':
newparts = (
[ValueTerminal('(', 'ptext')] +
[ValueTerminal(make_parenthesis_pairs(p), 'ptext')
if p.token_type == 'ptext' else p
for p in newparts] +
[ValueTerminal(')', 'ptext')])
if not part.as_ew_allowed:
wrap_as_ew_blocked += 1
newparts.append(end_ew_not_allowed)
Expand Down
12 changes: 11 additions & 1 deletion Lib/email/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
NLCRE = re.compile(r'\r\n|\r|\n')
fcre = re.compile(r'^From ', re.MULTILINE)
NEWLINE_WITHOUT_FWSP = re.compile(r'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')
NEWLINE_WITHOUT_FWSP_BYTES = re.compile(br'\r\n[^ \t]|\r[^ \n\t]|\n[^ \t]')


class Generator:
Expand Down Expand Up @@ -429,7 +430,16 @@ def _write_headers(self, msg):
# This is almost the same as the string version, except for handling
# strings with 8bit bytes.
for h, v in msg.raw_items():
self._fp.write(self.policy.fold_binary(h, v))
folded = self.policy.fold_binary(h, v)
if self.policy.verify_generated_headers:
linesep = self.policy.linesep.encode()
if not folded.endswith(linesep):
raise HeaderWriteError(
f'folded header does not end with {linesep!r}: {folded!r}')
if NEWLINE_WITHOUT_FWSP_BYTES.search(folded.removesuffix(linesep)):
raise HeaderWriteError(
f'folded header contains newline: {folded!r}')
self._fp.write(folded)
# A blank line always separates headers from body
self.write(self._NL)

Expand Down
Loading
Loading