summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorshadchin <[email protected]>2025-10-14 13:08:32 +0300
committershadchin <[email protected]>2025-10-14 13:28:36 +0300
commite9146d8a4d0ee112c89906f9fc8ce23b92250439 (patch)
treead04e44e21a1800ee1355d71cbb394fd33d14259
parenteeff8dc5b20a0ae455f23afec258a3d1b3f79730 (diff)
Update Python 3 to 3.12.12
commit_hash:ef97148212b5398ba2ed413d6056b6b8cf935cd8
-rw-r--r--contrib/tools/python3/.yandex_meta/override.nix4
-rw-r--r--contrib/tools/python3/Include/patchlevel.h4
-rw-r--r--contrib/tools/python3/Lib/html/parser.py251
-rw-r--r--contrib/tools/python3/Lib/pydoc_data/topics.py2
-rwxr-xr-xcontrib/tools/python3/Lib/tarfile.py3
-rw-r--r--contrib/tools/python3/Lib/ya.make4
-rw-r--r--contrib/tools/python3/Lib/zipfile/__init__.py51
-rw-r--r--contrib/tools/python3/Modules/_sqlite/ya.make4
-rw-r--r--contrib/tools/python3/Modules/pyexpat.c25
-rw-r--r--contrib/tools/python3/Parser/pegen.c3
-rw-r--r--contrib/tools/python3/Python/asm_trampoline.S22
-rw-r--r--contrib/tools/python3/README.rst2
-rw-r--r--contrib/tools/python3/bin/ya.make4
-rw-r--r--contrib/tools/python3/ya.make4
14 files changed, 257 insertions, 126 deletions
diff --git a/contrib/tools/python3/.yandex_meta/override.nix b/contrib/tools/python3/.yandex_meta/override.nix
index 5f9cd0e3489..f82a06fdcac 100644
--- a/contrib/tools/python3/.yandex_meta/override.nix
+++ b/contrib/tools/python3/.yandex_meta/override.nix
@@ -1,11 +1,11 @@
pkgs: attrs: with pkgs; with attrs; rec {
- version = "3.12.11";
+ version = "3.12.12";
src = fetchFromGitHub {
owner = "python";
repo = "cpython";
rev = "v${version}";
- hash = "sha256-vDczdMOTglDf5F+8PPkixvxScDCpedJCo0eL0VJJ/8g=";
+ hash = "sha256-7FUs8+cCpEtC39qIWpVvEkEl2cqNrxQlov4IwY4acT8=";
};
patches = [];
diff --git a/contrib/tools/python3/Include/patchlevel.h b/contrib/tools/python3/Include/patchlevel.h
index 45b770003cb..cafc3304a12 100644
--- a/contrib/tools/python3/Include/patchlevel.h
+++ b/contrib/tools/python3/Include/patchlevel.h
@@ -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 12
#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.12"
/*--end constants--*/
/* Version as a single 4-byte hex number, e.g. 0x010502B2 == 1.5.2b2.
diff --git a/contrib/tools/python3/Lib/html/parser.py b/contrib/tools/python3/Lib/html/parser.py
index 13c95c34e50..9b7556592ba 100644
--- a/contrib/tools/python3/Lib/html/parser.py
+++ b/contrib/tools/python3/Lib/html/parser.py
@@ -25,18 +25,48 @@ entityref = re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]')
charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]')
starttagopen = re.compile('<[a-zA-Z]')
+endtagopen = re.compile('</[a-zA-Z]')
piclose = re.compile('>')
-commentclose = re.compile(r'--\s*>')
+commentclose = re.compile(r'--!?>')
+commentabruptclose = re.compile(r'-?>')
# Note:
-# 1) if you change tagfind/attrfind remember to update locatestarttagend too;
-# 2) if you change tagfind/attrfind and/or locatestarttagend the parser will
+# 1) if you change tagfind/attrfind remember to update locatetagend too;
+# 2) if you change tagfind/attrfind and/or locatetagend the parser will
# explode, so don't do it.
-# see http://www.w3.org/TR/html5/tokenization.html#tag-open-state
-# and http://www.w3.org/TR/html5/tokenization.html#tag-name-state
-tagfind_tolerant = re.compile(r'([a-zA-Z][^\t\n\r\f />\x00]*)(?:\s|/(?!>))*')
-attrfind_tolerant = re.compile(
- r'((?<=[\'"\s/])[^\s/>][^\s/=>]*)(\s*=+\s*'
- r'(\'[^\']*\'|"[^"]*"|(?![\'"])[^>\s]*))?(?:\s|/(?!>))*')
+# see the HTML5 specs section "13.2.5.6 Tag open state",
+# "13.2.5.8 Tag name state" and "13.2.5.33 Attribute name state".
+# https://html.spec.whatwg.org/multipage/parsing.html#tag-open-state
+# https://html.spec.whatwg.org/multipage/parsing.html#tag-name-state
+# https://html.spec.whatwg.org/multipage/parsing.html#attribute-name-state
+tagfind_tolerant = re.compile(r'([a-zA-Z][^\t\n\r\f />]*)(?:[\t\n\r\f ]|/(?!>))*')
+attrfind_tolerant = re.compile(r"""
+ (
+ (?<=['"\t\n\r\f /])[^\t\n\r\f />][^\t\n\r\f /=>]* # attribute name
+ )
+ ([\t\n\r\f ]*=[\t\n\r\f ]* # value indicator
+ ('[^']*' # LITA-enclosed value
+ |"[^"]*" # LIT-enclosed value
+ |(?!['"])[^>\t\n\r\f ]* # bare value
+ )
+ )?
+ (?:[\t\n\r\f ]|/(?!>))* # possibly followed by a space
+""", re.VERBOSE)
+locatetagend = re.compile(r"""
+ [a-zA-Z][^\t\n\r\f />]* # tag name
+ [\t\n\r\f /]* # optional whitespace before attribute name
+ (?:(?<=['"\t\n\r\f /])[^\t\n\r\f />][^\t\n\r\f /=>]* # attribute name
+ (?:[\t\n\r\f ]*=[\t\n\r\f ]* # value indicator
+ (?:'[^']*' # LITA-enclosed value
+ |"[^"]*" # LIT-enclosed value
+ |(?!['"])[^>\t\n\r\f ]* # bare value
+ )
+ )?
+ [\t\n\r\f /]* # possibly followed by a space
+ )*
+ >?
+""", re.VERBOSE)
+# The following variables are not used, but are temporarily left for
+# backward compatibility.
locatestarttagend_tolerant = re.compile(r"""
<[a-zA-Z][^\t\n\r\f />\x00]* # tag name
(?:[\s/]* # optional whitespace before attribute name
@@ -53,8 +83,6 @@ locatestarttagend_tolerant = re.compile(r"""
\s* # trailing whitespace
""", re.VERBOSE)
endendtag = re.compile('>')
-# the HTML 5 spec, section 8.1.2.2, doesn't allow spaces between
-# </ and the tag name, so maybe this should be fixed
endtagfind = re.compile(r'</\s*([a-zA-Z][-.a-zA-Z0-9:_]*)\s*>')
@@ -82,6 +110,7 @@ class HTMLParser(_markupbase.ParserBase):
"""
CDATA_CONTENT_ELEMENTS = ("script", "style")
+ RCDATA_CONTENT_ELEMENTS = ("textarea", "title")
def __init__(self, *, convert_charrefs=True):
"""Initialize and reset this instance.
@@ -99,6 +128,8 @@ class HTMLParser(_markupbase.ParserBase):
self.lasttag = '???'
self.interesting = interesting_normal
self.cdata_elem = None
+ self._support_cdata = True
+ self._escapable = True
super().reset()
def feed(self, data):
@@ -120,13 +151,33 @@ class HTMLParser(_markupbase.ParserBase):
"""Return full source of start tag: '<...>'."""
return self.__starttag_text
- def set_cdata_mode(self, elem):
+ def set_cdata_mode(self, elem, *, escapable=False):
self.cdata_elem = elem.lower()
- self.interesting = re.compile(r'</\s*%s\s*>' % self.cdata_elem, re.I)
+ self._escapable = escapable
+ if escapable and not self.convert_charrefs:
+ self.interesting = re.compile(r'&|</%s(?=[\t\n\r\f />])' % self.cdata_elem,
+ re.IGNORECASE|re.ASCII)
+ else:
+ self.interesting = re.compile(r'</%s(?=[\t\n\r\f />])' % self.cdata_elem,
+ re.IGNORECASE|re.ASCII)
def clear_cdata_mode(self):
self.interesting = interesting_normal
self.cdata_elem = None
+ self._escapable = True
+
+ def _set_support_cdata(self, flag=True):
+ """Enable or disable support of the CDATA sections.
+ If enabled, "<[CDATA[" starts a CDATA section which ends with "]]>".
+ If disabled, "<[CDATA[" starts a bogus comments which ends with ">".
+
+ This method is not called by default. Its purpose is to be called
+ in custom handle_starttag() and handle_endtag() methods, with
+ value that depends on the adjusted current node.
+ See https://html.spec.whatwg.org/multipage/parsing.html#markup-declaration-open-state
+ for details.
+ """
+ self._support_cdata = flag
# Internal -- handle data as far as reasonable. May leave state
# and data to be processed by a subsequent call. If 'end' is
@@ -147,7 +198,7 @@ class HTMLParser(_markupbase.ParserBase):
# & near the end and see if it's followed by a space or ;.
amppos = rawdata.rfind('&', max(i, n-34))
if (amppos >= 0 and
- not re.compile(r'[\s;]').search(rawdata, amppos)):
+ not re.compile(r'[\t\n\r\f ;]').search(rawdata, amppos)):
break # wait till we get all the text
j = n
else:
@@ -159,7 +210,7 @@ class HTMLParser(_markupbase.ParserBase):
break
j = n
if i < j:
- if self.convert_charrefs and not self.cdata_elem:
+ if self.convert_charrefs and self._escapable:
self.handle_data(unescape(rawdata[i:j]))
else:
self.handle_data(rawdata[i:j])
@@ -177,7 +228,7 @@ class HTMLParser(_markupbase.ParserBase):
k = self.parse_pi(i)
elif startswith("<!", i):
k = self.parse_html_declaration(i)
- elif (i + 1) < n:
+ elif (i + 1) < n or end:
self.handle_data("<")
k = i + 1
else:
@@ -185,17 +236,35 @@ class HTMLParser(_markupbase.ParserBase):
if k < 0:
if not end:
break
- k = rawdata.find('>', i + 1)
- if k < 0:
- k = rawdata.find('<', i + 1)
- if k < 0:
- k = i + 1
- else:
- k += 1
- if self.convert_charrefs and not self.cdata_elem:
- self.handle_data(unescape(rawdata[i:k]))
+ if starttagopen.match(rawdata, i): # < + letter
+ pass
+ elif startswith("</", i):
+ if i + 2 == n:
+ self.handle_data("</")
+ elif endtagopen.match(rawdata, i): # </ + letter
+ pass
+ else:
+ # bogus comment
+ self.handle_comment(rawdata[i+2:])
+ elif startswith("<!--", i):
+ j = n
+ for suffix in ("--!", "--", "-"):
+ if rawdata.endswith(suffix, i+4):
+ j -= len(suffix)
+ break
+ self.handle_comment(rawdata[i+4:j])
+ elif startswith("<![CDATA[", i) and self._support_cdata:
+ self.unknown_decl(rawdata[i+3:])
+ elif rawdata[i:i+9].lower() == '<!doctype':
+ self.handle_decl(rawdata[i+2:])
+ elif startswith("<!", i):
+ # bogus comment
+ self.handle_comment(rawdata[i+2:])
+ elif startswith("<?", i):
+ self.handle_pi(rawdata[i+2:])
else:
- self.handle_data(rawdata[i:k])
+ raise AssertionError("we should not get here!")
+ k = n
i = self.updatepos(i, k)
elif startswith("&#", i):
match = charref.match(rawdata, i)
@@ -242,8 +311,8 @@ class HTMLParser(_markupbase.ParserBase):
else:
assert 0, "interesting.search() lied"
# end while
- if end and i < n and not self.cdata_elem:
- if self.convert_charrefs and not self.cdata_elem:
+ if end and i < n:
+ if self.convert_charrefs and self._escapable:
self.handle_data(unescape(rawdata[i:n]))
else:
self.handle_data(rawdata[i:n])
@@ -260,8 +329,12 @@ class HTMLParser(_markupbase.ParserBase):
if rawdata[i:i+4] == '<!--':
# this case is actually already handled in goahead()
return self.parse_comment(i)
- elif rawdata[i:i+3] == '<![':
- return self.parse_marked_section(i)
+ elif rawdata[i:i+9] == '<![CDATA[' and self._support_cdata:
+ j = rawdata.find(']]>', i+9)
+ if j < 0:
+ return -1
+ self.unknown_decl(rawdata[i+3: j])
+ return j + 3
elif rawdata[i:i+9].lower() == '<!doctype':
# find the closing >
gtpos = rawdata.find('>', i+9)
@@ -269,11 +342,35 @@ class HTMLParser(_markupbase.ParserBase):
return -1
self.handle_decl(rawdata[i+2:gtpos])
return gtpos+1
+ elif rawdata[i:i+3] == '<![':
+ j = rawdata.find('>', i+3)
+ if j < 0:
+ return -1
+ if rawdata[j-1] == ']':
+ self.unknown_decl(rawdata[i+3: j-1])
+ else:
+ self.handle_comment(rawdata[i+2: j])
+ return j + 1
else:
return self.parse_bogus_comment(i)
+ # Internal -- parse comment, return length or -1 if not terminated
+ # see https://html.spec.whatwg.org/multipage/parsing.html#comment-start-state
+ def parse_comment(self, i, report=True):
+ rawdata = self.rawdata
+ assert rawdata.startswith('<!--', i), 'unexpected call to parse_comment()'
+ match = commentclose.search(rawdata, i+4)
+ if not match:
+ match = commentabruptclose.match(rawdata, i+4)
+ if not match:
+ return -1
+ if report:
+ j = match.start()
+ self.handle_comment(rawdata[i+4: j])
+ return match.end()
+
# Internal -- parse bogus comment, return length or -1 if not terminated
- # see http://www.w3.org/TR/html5/tokenization.html#bogus-comment-state
+ # see https://html.spec.whatwg.org/multipage/parsing.html#bogus-comment-state
def parse_bogus_comment(self, i, report=1):
rawdata = self.rawdata
assert rawdata[i:i+2] in ('<!', '</'), ('unexpected call to '
@@ -299,6 +396,8 @@ class HTMLParser(_markupbase.ParserBase):
# Internal -- handle starttag, return end or -1 if not terminated
def parse_starttag(self, i):
+ # See the HTML5 specs section "13.2.5.8 Tag name state"
+ # https://html.spec.whatwg.org/multipage/parsing.html#tag-name-state
self.__starttag_text = None
endpos = self.check_for_whole_start_tag(i)
if endpos < 0:
@@ -338,82 +437,50 @@ class HTMLParser(_markupbase.ParserBase):
self.handle_starttag(tag, attrs)
if tag in self.CDATA_CONTENT_ELEMENTS:
self.set_cdata_mode(tag)
+ elif tag in self.RCDATA_CONTENT_ELEMENTS:
+ self.set_cdata_mode(tag, escapable=True)
return endpos
# Internal -- check to see if we have a complete starttag; return end
# or -1 if incomplete.
def check_for_whole_start_tag(self, i):
rawdata = self.rawdata
- m = locatestarttagend_tolerant.match(rawdata, i)
- if m:
- j = m.end()
- next = rawdata[j:j+1]
- if next == ">":
- return j + 1
- if next == "/":
- if rawdata.startswith("/>", j):
- return j + 2
- if rawdata.startswith("/", j):
- # buffer boundary
- return -1
- # else bogus input
- if j > i:
- return j
- else:
- return i + 1
- if next == "":
- # end of input
- return -1
- if next in ("abcdefghijklmnopqrstuvwxyz=/"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"):
- # end of input in or before attribute value, or we have the
- # '/' from a '/>' ending
- return -1
- if j > i:
- return j
- else:
- return i + 1
- raise AssertionError("we should not get here!")
+ match = locatetagend.match(rawdata, i+1)
+ assert match
+ j = match.end()
+ if rawdata[j-1] != ">":
+ return -1
+ return j
# Internal -- parse endtag, return end or -1 if incomplete
def parse_endtag(self, i):
+ # See the HTML5 specs section "13.2.5.7 End tag open state"
+ # https://html.spec.whatwg.org/multipage/parsing.html#end-tag-open-state
rawdata = self.rawdata
assert rawdata[i:i+2] == "</", "unexpected call to parse_endtag"
- match = endendtag.search(rawdata, i+1) # >
- if not match:
+ if rawdata.find('>', i+2) < 0: # fast check
return -1
- gtpos = match.end()
- match = endtagfind.match(rawdata, i) # </ + tag + >
- if not match:
- if self.cdata_elem is not None:
- self.handle_data(rawdata[i:gtpos])
- return gtpos
- # find the name: w3.org/TR/html5/tokenization.html#tag-name-state
- namematch = tagfind_tolerant.match(rawdata, i+2)
- if not namematch:
- # w3.org/TR/html5/tokenization.html#end-tag-open-state
- if rawdata[i:i+3] == '</>':
- return i+3
- else:
- return self.parse_bogus_comment(i)
- tagname = namematch.group(1).lower()
- # consume and ignore other stuff between the name and the >
- # Note: this is not 100% correct, since we might have things like
- # </tag attr=">">, but looking for > after the name should cover
- # most of the cases and is much simpler
- gtpos = rawdata.find('>', namematch.end())
- self.handle_endtag(tagname)
- return gtpos+1
+ if not endtagopen.match(rawdata, i): # </ + letter
+ if rawdata[i+2:i+3] == '>': # </> is ignored
+ # "missing-end-tag-name" parser error
+ return i+3
+ else:
+ return self.parse_bogus_comment(i)
- elem = match.group(1).lower() # script or style
- if self.cdata_elem is not None:
- if elem != self.cdata_elem:
- self.handle_data(rawdata[i:gtpos])
- return gtpos
+ match = locatetagend.match(rawdata, i+2)
+ assert match
+ j = match.end()
+ if rawdata[j-1] != ">":
+ return -1
- self.handle_endtag(elem)
+ # find the name: "13.2.5.8 Tag name state"
+ # https://html.spec.whatwg.org/multipage/parsing.html#tag-name-state
+ match = tagfind_tolerant.match(rawdata, i+2)
+ assert match
+ tag = match.group(1).lower()
+ self.handle_endtag(tag)
self.clear_cdata_mode()
- return gtpos
+ return j
# Overridable -- finish processing of start+end tag: <tag.../>
def handle_startendtag(self, tag, attrs):
diff --git a/contrib/tools/python3/Lib/pydoc_data/topics.py b/contrib/tools/python3/Lib/pydoc_data/topics.py
index 742866a1aa8..3593d44cc5c 100644
--- a/contrib/tools/python3/Lib/pydoc_data/topics.py
+++ b/contrib/tools/python3/Lib/pydoc_data/topics.py
@@ -1,4 +1,4 @@
-# Autogenerated by Sphinx on Tue Jun 3 17:41:43 2025
+# Autogenerated by Sphinx on Thu Oct 9 13:06:56 2025
# as part of the release process.
topics = {
diff --git a/contrib/tools/python3/Lib/tarfile.py b/contrib/tools/python3/Lib/tarfile.py
index 9999a99d54d..59d3f6e5cce 100755
--- a/contrib/tools/python3/Lib/tarfile.py
+++ b/contrib/tools/python3/Lib/tarfile.py
@@ -1615,6 +1615,9 @@ class TarInfo(object):
"""Round up a byte count by BLOCKSIZE and return it,
e.g. _block(834) => 1024.
"""
+ # Only non-negative offsets are allowed
+ if count < 0:
+ raise InvalidHeaderError("invalid offset")
blocks, remainder = divmod(count, BLOCKSIZE)
if remainder:
blocks += 1
diff --git a/contrib/tools/python3/Lib/ya.make b/contrib/tools/python3/Lib/ya.make
index 733ef64fc8c..8c7a80c5b1b 100644
--- a/contrib/tools/python3/Lib/ya.make
+++ b/contrib/tools/python3/Lib/ya.make
@@ -4,9 +4,9 @@ ENABLE(PYBUILD_NO_PY)
PY3_LIBRARY()
-VERSION(3.12.11)
+VERSION(3.12.12)
-ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.11.tar.gz)
+ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.12.tar.gz)
LICENSE(Python-2.0)
diff --git a/contrib/tools/python3/Lib/zipfile/__init__.py b/contrib/tools/python3/Lib/zipfile/__init__.py
index 5d87da6d721..e4bf7e97fbd 100644
--- a/contrib/tools/python3/Lib/zipfile/__init__.py
+++ b/contrib/tools/python3/Lib/zipfile/__init__.py
@@ -231,7 +231,7 @@ def is_zipfile(filename):
else:
with open(filename, "rb") as fp:
result = _check_zipfile(fp)
- except OSError:
+ except (OSError, BadZipFile):
pass
return result
@@ -239,16 +239,15 @@ def _EndRecData64(fpin, offset, endrec):
"""
Read the ZIP64 end-of-archive records and use that to update endrec
"""
- try:
- fpin.seek(offset - sizeEndCentDir64Locator, 2)
- except OSError:
- # If the seek fails, the file is not large enough to contain a ZIP64
+ offset -= sizeEndCentDir64Locator
+ if offset < 0:
+ # The file is not large enough to contain a ZIP64
# end-of-archive record, so just return the end record we were given.
return endrec
-
+ fpin.seek(offset)
data = fpin.read(sizeEndCentDir64Locator)
if len(data) != sizeEndCentDir64Locator:
- return endrec
+ raise OSError("Unknown I/O error")
sig, diskno, reloff, disks = struct.unpack(structEndArchive64Locator, data)
if sig != stringEndArchive64Locator:
return endrec
@@ -256,16 +255,33 @@ def _EndRecData64(fpin, offset, endrec):
if diskno != 0 or disks > 1:
raise BadZipFile("zipfiles that span multiple disks are not supported")
- # Assume no 'zip64 extensible data'
- fpin.seek(offset - sizeEndCentDir64Locator - sizeEndCentDir64, 2)
+ offset -= sizeEndCentDir64
+ if reloff > offset:
+ raise BadZipFile("Corrupt zip64 end of central directory locator")
+ # First, check the assumption that there is no prepended data.
+ fpin.seek(reloff)
+ extrasz = offset - reloff
data = fpin.read(sizeEndCentDir64)
if len(data) != sizeEndCentDir64:
- return endrec
+ raise OSError("Unknown I/O error")
+ if not data.startswith(stringEndArchive64) and reloff != offset:
+ # Since we already have seen the Zip64 EOCD Locator, it's
+ # possible we got here because there is prepended data.
+ # Assume no 'zip64 extensible data'
+ fpin.seek(offset)
+ extrasz = 0
+ data = fpin.read(sizeEndCentDir64)
+ if len(data) != sizeEndCentDir64:
+ raise OSError("Unknown I/O error")
+ if not data.startswith(stringEndArchive64):
+ raise BadZipFile("Zip64 end of central directory record not found")
+
sig, sz, create_version, read_version, disk_num, disk_dir, \
dircount, dircount2, dirsize, diroffset = \
struct.unpack(structEndArchive64, data)
- if sig != stringEndArchive64:
- return endrec
+ if (diroffset + dirsize != reloff or
+ sz + 12 != sizeEndCentDir64 + extrasz):
+ raise BadZipFile("Corrupt zip64 end of central directory record")
# Update the original endrec using data from the ZIP64 record
endrec[_ECD_SIGNATURE] = sig
@@ -275,6 +291,7 @@ def _EndRecData64(fpin, offset, endrec):
endrec[_ECD_ENTRIES_TOTAL] = dircount2
endrec[_ECD_SIZE] = dirsize
endrec[_ECD_OFFSET] = diroffset
+ endrec[_ECD_LOCATION] = offset - extrasz
return endrec
@@ -308,7 +325,7 @@ def _EndRecData(fpin):
endrec.append(filesize - sizeEndCentDir)
# Try to read the "Zip64 end of central directory" structure
- return _EndRecData64(fpin, -sizeEndCentDir, endrec)
+ return _EndRecData64(fpin, filesize - sizeEndCentDir, endrec)
# Either this is not a ZIP file, or it is a ZIP file with an archive
# comment. Search the end of the file for the "end of central directory"
@@ -332,8 +349,7 @@ def _EndRecData(fpin):
endrec.append(maxCommentStart + start)
# Try to read the "Zip64 end of central directory" structure
- return _EndRecData64(fpin, maxCommentStart + start - filesize,
- endrec)
+ return _EndRecData64(fpin, maxCommentStart + start, endrec)
# Unable to find a valid end of central directory structure
return None
@@ -1427,9 +1443,6 @@ class ZipFile:
# "concat" is zero, unless zip was concatenated to another file
concat = endrec[_ECD_LOCATION] - size_cd - offset_cd
- if endrec[_ECD_SIGNATURE] == stringEndArchive64:
- # If Zip64 extension structures are present, account for them
- concat -= (sizeEndCentDir64 + sizeEndCentDir64Locator)
if self.debug > 2:
inferred = concat + offset_cd
@@ -2047,7 +2060,7 @@ class ZipFile:
" would require ZIP64 extensions")
zip64endrec = struct.pack(
structEndArchive64, stringEndArchive64,
- 44, 45, 45, 0, 0, centDirCount, centDirCount,
+ sizeEndCentDir64 - 12, 45, 45, 0, 0, centDirCount, centDirCount,
centDirSize, centDirOffset)
self.fp.write(zip64endrec)
diff --git a/contrib/tools/python3/Modules/_sqlite/ya.make b/contrib/tools/python3/Modules/_sqlite/ya.make
index 5f827aaf5ac..bf957b0a3aa 100644
--- a/contrib/tools/python3/Modules/_sqlite/ya.make
+++ b/contrib/tools/python3/Modules/_sqlite/ya.make
@@ -2,9 +2,9 @@
PY3_LIBRARY()
-VERSION(3.12.11)
+VERSION(3.12.12)
-ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.11.tar.gz)
+ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.12.tar.gz)
LICENSE(Python-2.0)
diff --git a/contrib/tools/python3/Modules/pyexpat.c b/contrib/tools/python3/Modules/pyexpat.c
index b354a86e7f9..d941d25b79b 100644
--- a/contrib/tools/python3/Modules/pyexpat.c
+++ b/contrib/tools/python3/Modules/pyexpat.c
@@ -69,6 +69,15 @@ typedef struct {
PyObject_HEAD
XML_Parser itself;
+ /*
+ * Strong reference to a parent `xmlparseobject` if this parser
+ * is a child parser. Set to NULL if this parser is a root parser.
+ * This is needed to keep the parent parser alive as long as it has
+ * at least one child parser.
+ *
+ * See https://github.com/python/cpython/issues/139400 for details.
+ */
+ PyObject *parent;
int ordered_attributes; /* Return attributes as a list. */
int specified_attributes; /* Report only specified attributes. */
int in_callback; /* Is a callback active? */
@@ -990,6 +999,11 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self,
return NULL;
}
+ // The new subparser will make use of the parent XML_Parser inside of Expat.
+ // So we need to take subparsers into account with the reference counting
+ // of their parent parser.
+ Py_INCREF(self);
+
new_parser->buffer_size = self->buffer_size;
new_parser->buffer_used = 0;
new_parser->buffer = NULL;
@@ -999,6 +1013,7 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self,
new_parser->ns_prefixes = self->ns_prefixes;
new_parser->itself = XML_ExternalEntityParserCreate(self->itself, context,
encoding);
+ new_parser->parent = (PyObject *)self;
new_parser->handlers = 0;
new_parser->intern = Py_XNewRef(self->intern);
@@ -1006,11 +1021,13 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self,
new_parser->buffer = PyMem_Malloc(new_parser->buffer_size);
if (new_parser->buffer == NULL) {
Py_DECREF(new_parser);
+ Py_DECREF(self);
return PyErr_NoMemory();
}
}
if (!new_parser->itself) {
Py_DECREF(new_parser);
+ Py_DECREF(self);
return PyErr_NoMemory();
}
@@ -1023,6 +1040,7 @@ pyexpat_xmlparser_ExternalEntityParserCreate_impl(xmlparseobject *self,
new_parser->handlers = PyMem_New(PyObject *, i);
if (!new_parser->handlers) {
Py_DECREF(new_parser);
+ Py_DECREF(self);
return PyErr_NoMemory();
}
clear_handlers(new_parser, 1);
@@ -1212,6 +1230,7 @@ newxmlparseobject(pyexpat_state *state, const char *encoding,
/* namespace_separator is either NULL or contains one char + \0 */
self->itself = XML_ParserCreate_MM(encoding, &ExpatMemoryHandler,
namespace_separator);
+ self->parent = NULL;
if (self->itself == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"XML_ParserCreate failed");
@@ -1247,6 +1266,7 @@ xmlparse_traverse(xmlparseobject *op, visitproc visit, void *arg)
for (int i = 0; handler_info[i].name != NULL; i++) {
Py_VISIT(op->handlers[i]);
}
+ Py_VISIT(op->parent);
Py_VISIT(Py_TYPE(op));
return 0;
}
@@ -1256,6 +1276,10 @@ xmlparse_clear(xmlparseobject *op)
{
clear_handlers(op, 0);
Py_CLEAR(op->intern);
+ // NOTE: We cannot call Py_CLEAR(op->parent) prior to calling
+ // XML_ParserFree(op->itself), or a subparser could lose its parent
+ // XML_Parser while still making use of it internally.
+ // https://github.com/python/cpython/issues/139400
return 0;
}
@@ -1267,6 +1291,7 @@ xmlparse_dealloc(xmlparseobject *self)
if (self->itself != NULL)
XML_ParserFree(self->itself);
self->itself = NULL;
+ Py_CLEAR(self->parent);
if (self->handlers != NULL) {
PyMem_Free(self->handlers);
diff --git a/contrib/tools/python3/Parser/pegen.c b/contrib/tools/python3/Parser/pegen.c
index 5b92133d9f9..fa1a4280019 100644
--- a/contrib/tools/python3/Parser/pegen.c
+++ b/contrib/tools/python3/Parser/pegen.c
@@ -637,7 +637,8 @@ expr_ty _PyPegen_soft_keyword_token(Parser *p) {
Py_ssize_t size;
PyBytes_AsStringAndSize(t->bytes, &the_token, &size);
for (char **keyword = p->soft_keywords; *keyword != NULL; keyword++) {
- if (strncmp(*keyword, the_token, size) == 0) {
+ if (strlen(*keyword) == (size_t)size &&
+ strncmp(*keyword, the_token, (size_t)size) == 0) {
return _PyPegen_name_from_token(p, t);
}
}
diff --git a/contrib/tools/python3/Python/asm_trampoline.S b/contrib/tools/python3/Python/asm_trampoline.S
index 460707717df..341d0bbe51f 100644
--- a/contrib/tools/python3/Python/asm_trampoline.S
+++ b/contrib/tools/python3/Python/asm_trampoline.S
@@ -9,6 +9,9 @@
# }
_Py_trampoline_func_start:
#ifdef __x86_64__
+#if defined(__CET__) && (__CET__ & 1)
+ endbr64
+#endif
sub $8, %rsp
call *%rcx
add $8, %rsp
@@ -26,3 +29,22 @@ _Py_trampoline_func_start:
.globl _Py_trampoline_func_end
_Py_trampoline_func_end:
.section .note.GNU-stack,"",@progbits
+# Note for indicating the assembly code supports CET
+#if defined(__x86_64__) && defined(__CET__) && (__CET__ & 1)
+ .section .note.gnu.property,"a"
+ .align 8
+ .long 1f - 0f
+ .long 4f - 1f
+ .long 5
+0:
+ .string "GNU"
+1:
+ .align 8
+ .long 0xc0000002
+ .long 3f - 2f
+2:
+ .long 0x3
+3:
+ .align 8
+4:
+#endif // __x86_64__
diff --git a/contrib/tools/python3/README.rst b/contrib/tools/python3/README.rst
index a01b2519de6..7a384ed7833 100644
--- a/contrib/tools/python3/README.rst
+++ b/contrib/tools/python3/README.rst
@@ -1,4 +1,4 @@
-This is Python version 3.12.11
+This is Python version 3.12.12
==============================
.. image:: https://github.com/python/cpython/workflows/Tests/badge.svg
diff --git a/contrib/tools/python3/bin/ya.make b/contrib/tools/python3/bin/ya.make
index 79cef8eb2a9..2aee1f7f72b 100644
--- a/contrib/tools/python3/bin/ya.make
+++ b/contrib/tools/python3/bin/ya.make
@@ -2,9 +2,9 @@
PY3_PROGRAM(python3)
-VERSION(3.12.11)
+VERSION(3.12.12)
-ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.11.tar.gz)
+ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.12.tar.gz)
LICENSE(Python-2.0)
diff --git a/contrib/tools/python3/ya.make b/contrib/tools/python3/ya.make
index 53ed24e857c..9d31cd2121a 100644
--- a/contrib/tools/python3/ya.make
+++ b/contrib/tools/python3/ya.make
@@ -2,9 +2,9 @@
LIBRARY()
-VERSION(3.12.11)
+VERSION(3.12.12)
-ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.11.tar.gz)
+ORIGINAL_SOURCE(https://github.com/python/cpython/archive/v3.12.12.tar.gz)
LICENSE(Python-2.0)