diff options
| author | Alexander Smirnov <[email protected]> | 2025-07-22 16:16:01 +0000 |
|---|---|---|
| committer | Alexander Smirnov <[email protected]> | 2025-07-22 16:16:01 +0000 |
| commit | 6bf496db7343a23f405f67a79b4198e06fdd7f42 (patch) | |
| tree | ebbfbb677ebb39843c45be8a5c37d5f9066fc0ee /contrib/python | |
| parent | 81c6c7f73ad64139f8cbb947130d639554d3bc79 (diff) | |
| parent | 3102e1630f400423c5b7b71a1d9254680d6d9731 (diff) | |
Merge branch 'rightlib' into merge-libs-250722-1614
Diffstat (limited to 'contrib/python')
24 files changed, 609 insertions, 98 deletions
diff --git a/contrib/python/fonttools/.dist-info/METADATA b/contrib/python/fonttools/.dist-info/METADATA index ddc8be2d7de..164ca311dc3 100644 --- a/contrib/python/fonttools/.dist-info/METADATA +++ b/contrib/python/fonttools/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: fonttools -Version: 4.58.4 +Version: 4.58.5 Summary: Tools to manipulate font files Home-page: http://github.com/fonttools/fonttools Author: Just van Rossum @@ -388,6 +388,13 @@ Have fun! Changelog ~~~~~~~~~ +4.58.5 (released 2025-07-03) +---------------------------- + +- [feaLib] Don't try to combine ligature & multisub rules (#3874). +- [feaLib/ast] Use weakref proxies to avoid cycles in visitor (#3873). +- [varLib.instancer] Fixed instancing CFF2 fonts where VarData contains more than 64k items (#3858). + 4.58.4 (released 2025-06-13) ---------------------------- diff --git a/contrib/python/fonttools/fontTools/__init__.py b/contrib/python/fonttools/fontTools/__init__.py index 155d9250220..306cd68a6be 100644 --- a/contrib/python/fonttools/fontTools/__init__.py +++ b/contrib/python/fonttools/fontTools/__init__.py @@ -3,6 +3,6 @@ from fontTools.misc.loggingTools import configLogger log = logging.getLogger(__name__) -version = __version__ = "4.58.4" +version = __version__ = "4.58.5" __all__ = ["version", "log", "configLogger"] diff --git a/contrib/python/fonttools/fontTools/feaLib/ast.py b/contrib/python/fonttools/fontTools/feaLib/ast.py index 9663f73b4c8..2624e6b9d6d 100644 --- a/contrib/python/fonttools/fontTools/feaLib/ast.py +++ b/contrib/python/fonttools/fontTools/feaLib/ast.py @@ -1,3 +1,4 @@ +import weakref from fontTools.feaLib.error import FeatureLibError from fontTools.feaLib.location import FeatureLibLocation from fontTools.misc.encodingTools import getEncoding @@ -529,7 +530,7 @@ class MarkClass(object): def addDefinition(self, definition): """Add a :class:`MarkClassDefinition` statement to this mark class.""" assert isinstance(definition, MarkClassDefinition) - self.definitions.append(definition) + self.definitions.append(weakref.proxy(definition)) for glyph in definition.glyphSet(): if glyph in self.glyphs: otherLoc = self.glyphs[glyph].location diff --git a/contrib/python/fonttools/fontTools/feaLib/builder.py b/contrib/python/fonttools/fontTools/feaLib/builder.py index ce9515bd68d..25b319c0b01 100644 --- a/contrib/python/fonttools/fontTools/feaLib/builder.py +++ b/contrib/python/fonttools/fontTools/feaLib/builder.py @@ -259,12 +259,13 @@ class Builder(object): key = (script, lang, feature_name) self.features_.setdefault(key, []).append(lookup) - def get_lookup_(self, location, builder_class): + def get_lookup_(self, location, builder_class, mapping=None): if ( self.cur_lookup_ and type(self.cur_lookup_) == builder_class and self.cur_lookup_.lookupflag == self.lookupflag_ and self.cur_lookup_.markFilterSet == self.lookupflag_markFilterSet_ + and self.cur_lookup_.can_add_mapping(mapping) ): return self.cur_lookup_ if self.cur_lookup_name_ and self.cur_lookup_: @@ -1305,7 +1306,7 @@ class Builder(object): # GSUB rules def add_any_subst_(self, location, mapping): - lookup = self.get_lookup_(location, AnySubstBuilder) + lookup = self.get_lookup_(location, AnySubstBuilder, mapping=mapping) for key, value in mapping.items(): if key in lookup.mapping: if value == lookup.mapping[key]: diff --git a/contrib/python/fonttools/fontTools/misc/visitor.py b/contrib/python/fonttools/fontTools/misc/visitor.py index 6de432ef934..65f305632de 100644 --- a/contrib/python/fonttools/fontTools/misc/visitor.py +++ b/contrib/python/fonttools/fontTools/misc/visitor.py @@ -1,11 +1,19 @@ """Generic visitor pattern implementation for Python objects.""" import enum +import weakref class Visitor(object): defaultStop = False + _visitors = { + # By default we skip visiting weak references to avoid recursion + # issues. Users can override this by registering a visit + # function for weakref.ProxyType. + weakref.ProxyType: {None: lambda self, obj, *args, **kwargs: False} + } + @classmethod def _register(celf, clazzes_attrs): assert celf != Visitor, "Subclass Visitor instead." diff --git a/contrib/python/fonttools/fontTools/otlLib/builder.py b/contrib/python/fonttools/fontTools/otlLib/builder.py index da00e9c5ebd..c4b482f02b4 100644 --- a/contrib/python/fonttools/fontTools/otlLib/builder.py +++ b/contrib/python/fonttools/fontTools/otlLib/builder.py @@ -256,6 +256,10 @@ class LookupBuilder(object): ) ) + def can_add_mapping(self, _mapping) -> bool: + # used by AnySubstBuilder, below + return True + class AlternateSubstBuilder(LookupBuilder): """Builds an Alternate Substitution (GSUB3) lookup. @@ -1376,6 +1380,32 @@ class AnySubstBuilder(LookupBuilder): def _add_to_ligature_subst(self, builder, key, value): builder.ligatures[key] = value[0] + def can_add_mapping(self, mapping) -> bool: + if mapping is None: + return True + # single sub rules can be treated as (degenerate) liga-or-multi sub + # rules, but multi and liga sub rules themselves have incompatible + # representations. It is uncommon that these are in the same set of + # rules, but it happens. + is_multi = any(len(v) > 1 for v in mapping.values()) + is_liga = any(len(k) > 1 for k in mapping.keys()) + + has_existing_multi = False + has_existing_liga = False + + for k, v in self.mapping.items(): + if k[0] == self.SUBTABLE_BREAK_: + continue + if len(k) > 1: + has_existing_liga = True + if len(v) > 1: + has_existing_multi = True + + can_reuse = not (has_existing_multi and is_liga) and not ( + has_existing_liga and is_multi + ) + return can_reuse + def promote_lookup_type(self, is_named_lookup): # https://github.com/fonttools/fonttools/issues/612 # A multiple substitution may have a single destination, in which case diff --git a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py index a0b832a3c18..76901880553 100644 --- a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py +++ b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py @@ -723,9 +723,7 @@ def instantiateCFF2( minor = varDataCursor[major] varDataCursor[major] += 1 - varIdx = (major << 16) + minor - - defaultValue += round(defaultDeltas[varIdx]) + defaultValue += round(defaultDeltas[major][minor]) newDefaults.append(defaultValue) varData = varStore.VarData[major] @@ -781,7 +779,9 @@ def instantiateCFF2( storeBlendsToVarStore(value + [count]) # Instantiate VarStore - defaultDeltas = instantiateItemVariationStore(varStore, fvarAxes, axisLimits) + defaultDeltas = instantiateItemVariationStore( + varStore, fvarAxes, axisLimits, hierarchical=True + ) # Read back new charstring blends from the instantiated VarStore varDataCursor = [0] * len(varStore.VarData) @@ -839,18 +839,13 @@ def instantiateCFF2( varData.Item = [] varData.ItemCount = 0 - # Remove vsindex commands that are no longer needed, collect those that are. - usedVsindex = set() - for commands in allCommands: - if any(isinstance(arg, list) for command in commands for arg in command[1]): - vsindex = 0 - for command in commands: - if command[0] == "vsindex": - vsindex = command[1][0] - continue - if any(isinstance(arg, list) for arg in command[1]): - usedVsindex.add(vsindex) - else: + # Collect surviving vsindexes + usedVsindex = set( + i for i in range(len(varStore.VarData)) if varStore.VarData[i].VarRegionCount + ) + # Remove vsindex commands that are no longer needed + for commands, private in zip(allCommands, allCommandPrivates): + if not any(isinstance(arg, list) for command in commands for arg in command[1]): commands[:] = [command for command in commands if command[0] != "vsindex"] # Remove unused VarData and update vsindex values @@ -863,10 +858,14 @@ def instantiateCFF2( for command in commands: if command[0] == "vsindex": command[1][0] = vsindexMapping[command[1][0]] + for private in privateDicts: + if hasattr(private, "vsindex"): + private.vsindex = vsindexMapping[private.vsindex] # Remove initial vsindex commands that are implied - for commands in allCommands: - if commands and commands[0] == ("vsindex", [0]): + for commands, private in zip(allCommands, allCommandPrivates): + vsindex = getattr(private, "vsindex", 0) + if commands and commands[0] == ("vsindex", [vsindex]): commands.pop(0) # Ship the charstrings! @@ -1247,7 +1246,9 @@ class _TupleVarStoreAdapter(object): return itemVarStore -def instantiateItemVariationStore(itemVarStore, fvarAxes, axisLimits): +def instantiateItemVariationStore( + itemVarStore, fvarAxes, axisLimits, hierarchical=False +): """Compute deltas at partial location, and update varStore in-place. Remove regions in which all axes were instanced, or fall outside the new axis @@ -1279,12 +1280,19 @@ def instantiateItemVariationStore(itemVarStore, fvarAxes, axisLimits): assert itemVarStore.VarDataCount == newItemVarStore.VarDataCount itemVarStore.VarData = newItemVarStore.VarData - defaultDeltas = { - ((major << 16) + minor): delta - for major, deltas in enumerate(defaultDeltaArray) - for minor, delta in enumerate(deltas) - } - defaultDeltas[itemVarStore.NO_VARIATION_INDEX] = 0 + if not hierarchical: + defaultDeltas = { + ((major << 16) + minor): delta + for major, deltas in enumerate(defaultDeltaArray) + for minor, delta in enumerate(deltas) + } + defaultDeltas[itemVarStore.NO_VARIATION_INDEX] = 0 + else: + defaultDeltas = {0xFFFF: {0xFFFF: 0}} # NO_VARIATION_INDEX + for major, deltas in enumerate(defaultDeltaArray): + defaultDeltasForMajor = defaultDeltas.setdefault(major, {}) + for minor, delta in enumerate(deltas): + defaultDeltasForMajor[minor] = delta return defaultDeltas diff --git a/contrib/python/fonttools/ya.make b/contrib/python/fonttools/ya.make index f2a7b5ccf30..e114871a154 100644 --- a/contrib/python/fonttools/ya.make +++ b/contrib/python/fonttools/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(4.58.4) +VERSION(4.58.5) LICENSE(MIT) diff --git a/contrib/python/future/py3/future/backports/email/message.py b/contrib/python/future/py3/future/backports/email/message.py index d8d9615d7d7..8b4a974944b 100644 --- a/contrib/python/future/py3/future/backports/email/message.py +++ b/contrib/python/future/py3/future/backports/email/message.py @@ -10,7 +10,6 @@ from future.builtins import list, range, str, zip __all__ = ['Message'] import re -import uu import base64 import binascii from io import BytesIO, StringIO @@ -106,6 +105,37 @@ def _unquotevalue(value): return utils.unquote(value) +def _decode_uu(encoded): + """Decode uuencoded data.""" + decoded_lines = [] + encoded_lines_iter = iter(encoded.splitlines()) + for line in encoded_lines_iter: + if line.startswith(b"begin "): + mode, _, path = line.removeprefix(b"begin ").partition(b" ") + try: + int(mode, base=8) + except ValueError: + continue + else: + break + else: + raise ValueError("`begin` line not found") + for line in encoded_lines_iter: + if not line: + raise ValueError("Truncated input") + elif line.strip(b' \t\r\n\f') == b'end': + break + try: + decoded_line = binascii.a2b_uu(line) + except binascii.Error: + # Workaround for broken uuencoders by /Fredrik Lundh + nbytes = (((line[0]-32) & 63) * 4 + 5) // 3 + decoded_line = binascii.a2b_uu(line[:nbytes]) + decoded_lines.append(decoded_line) + + return b''.join(decoded_lines) + + class Message(object): """Basic message object. @@ -262,13 +292,10 @@ class Message(object): self.policy.handle_defect(self, defect) return value elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): - in_file = BytesIO(bpayload) - out_file = BytesIO() try: - uu.decode(in_file, out_file, quiet=True) - return out_file.getvalue() - except uu.Error: - # Some decoding problem + return _decode_uu(bpayload) + except ValueError: + # Some decoding problem. return bpayload if isinstance(payload, str): return bpayload diff --git a/contrib/python/future/py3/future/backports/email/mime/audio.py b/contrib/python/future/py3/future/backports/email/mime/audio.py index 4989c114207..0cd703fdc31 100644 --- a/contrib/python/future/py3/future/backports/email/mime/audio.py +++ b/contrib/python/future/py3/future/backports/email/mime/audio.py @@ -9,37 +9,11 @@ from __future__ import absolute_import __all__ = ['MIMEAudio'] -import sndhdr - from io import BytesIO from future.backports.email import encoders from future.backports.email.mime.nonmultipart import MIMENonMultipart -_sndhdr_MIMEmap = {'au' : 'basic', - 'wav' :'x-wav', - 'aiff':'x-aiff', - 'aifc':'x-aiff', - } - -# There are others in sndhdr that don't have MIME types. :( -# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? -def _whatsnd(data): - """Try to identify a sound file type. - - sndhdr.what() has a pretty cruddy interface, unfortunately. This is why - we re-do it here. It would be easier to reverse engineer the Unix 'file' - command and use the standard 'magic' file, as shipped with a modern Unix. - """ - hdr = data[:512] - fakefile = BytesIO(hdr) - for testfn in sndhdr.tests: - res = testfn(hdr, fakefile) - if res is not None: - return _sndhdr_MIMEmap.get(res[0]) - return None - - class MIMEAudio(MIMENonMultipart): """Class for generating audio/* MIME documents.""" @@ -66,9 +40,61 @@ class MIMEAudio(MIMENonMultipart): header. """ if _subtype is None: - _subtype = _whatsnd(_audiodata) + _subtype = _what(_audiodata) if _subtype is None: raise TypeError('Could not find audio MIME subtype') MIMENonMultipart.__init__(self, 'audio', _subtype, **_params) self.set_payload(_audiodata) _encoder(self) + + +_rules = [] + + +# Originally from the sndhdr module. +# +# There are others in sndhdr that don't have MIME types. :( +# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? +def _what(data): + # Try to identify a sound file type. + # + # sndhdr.what() had a pretty cruddy interface, unfortunately. This is why + # we re-do it here. It would be easier to reverse engineer the Unix 'file' + # command and use the standard 'magic' file, as shipped with a modern Unix. + for testfn in _rules: + if res := testfn(data): + return res + else: + return None + + +def rule(rulefunc): + _rules.append(rulefunc) + return rulefunc + + +@rule +def _aiff(h): + if not h.startswith(b'FORM'): + return None + if h[8:12] in {b'AIFC', b'AIFF'}: + return 'x-aiff' + else: + return None + + +@rule +def _au(h): + if h.startswith(b'.snd'): + return 'basic' + else: + return None + + +@rule +def _wav(h): + # 'RIFF' <len> 'WAVE' 'fmt ' <len> + if not h.startswith(b'RIFF') or h[8:12] != b'WAVE' or h[12:16] != b'fmt ': + return None + else: + return "x-wav" diff --git a/contrib/python/future/py3/future/backports/email/mime/image.py b/contrib/python/future/py3/future/backports/email/mime/image.py index a03602464aa..b8fa87930f0 100644 --- a/contrib/python/future/py3/future/backports/email/mime/image.py +++ b/contrib/python/future/py3/future/backports/email/mime/image.py @@ -9,8 +9,6 @@ from __future__ import absolute_import __all__ = ['MIMEImage'] -import imghdr - from future.backports.email import encoders from future.backports.email.mime.nonmultipart import MIMENonMultipart @@ -40,9 +38,118 @@ class MIMEImage(MIMENonMultipart): header. """ if _subtype is None: - _subtype = imghdr.what(None, _imagedata) + _subtype = _what(_imagedata) if _subtype is None: raise TypeError('Could not guess image MIME subtype') MIMENonMultipart.__init__(self, 'image', _subtype, **_params) self.set_payload(_imagedata) _encoder(self) + + +_rules = [] + + +# Originally from the imghdr module. +def _what(data): + for rule in _rules: + if res := rule(data): + return res + else: + return None + + +def rule(rulefunc): + _rules.append(rulefunc) + return rulefunc + + +@rule +def _jpeg(h): + """JPEG data with JFIF or Exif markers; and raw JPEG""" + if h[6:10] in (b'JFIF', b'Exif'): + return 'jpeg' + elif h[:4] == b'\xff\xd8\xff\xdb': + return 'jpeg' + + +@rule +def _png(h): + if h.startswith(b'\211PNG\r\n\032\n'): + return 'png' + + +@rule +def _gif(h): + """GIF ('87 and '89 variants)""" + if h[:6] in (b'GIF87a', b'GIF89a'): + return 'gif' + + +@rule +def _tiff(h): + """TIFF (can be in Motorola or Intel byte order)""" + if h[:2] in (b'MM', b'II'): + return 'tiff' + + +@rule +def _rgb(h): + """SGI image library""" + if h.startswith(b'\001\332'): + return 'rgb' + + +@rule +def _pbm(h): + """PBM (portable bitmap)""" + if len(h) >= 3 and \ + h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r': + return 'pbm' + + +@rule +def _pgm(h): + """PGM (portable graymap)""" + if len(h) >= 3 and \ + h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r': + return 'pgm' + + +@rule +def _ppm(h): + """PPM (portable pixmap)""" + if len(h) >= 3 and \ + h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r': + return 'ppm' + + +@rule +def _rast(h): + """Sun raster file""" + if h.startswith(b'\x59\xA6\x6A\x95'): + return 'rast' + + +@rule +def _xbm(h): + """X bitmap (X10 or X11)""" + if h.startswith(b'#define '): + return 'xbm' + + +@rule +def _bmp(h): + if h.startswith(b'BM'): + return 'bmp' + + +@rule +def _webp(h): + if h.startswith(b'RIFF') and h[8:12] == b'WEBP': + return 'webp' + + +@rule +def _exr(h): + if h.startswith(b'\x76\x2f\x31\x01'): + return 'exr' diff --git a/contrib/python/future/py3/patches/07-support-python-3.13.patch b/contrib/python/future/py3/patches/07-support-python-3.13.patch new file mode 100644 index 00000000000..c0e15bbbc33 --- /dev/null +++ b/contrib/python/future/py3/patches/07-support-python-3.13.patch @@ -0,0 +1,65 @@ +--- contrib/python/future/py3/future/backports/email/message.py (index) ++++ contrib/python/future/py3/future/backports/email/message.py (working tree) +@@ -10,7 +10,6 @@ from future.builtins import list, range, str, zip + __all__ = ['Message'] + + import re +-import uu + import base64 + import binascii + from io import BytesIO, StringIO +@@ -106,6 +105,37 @@ def _unquotevalue(value): + return utils.unquote(value) + + ++def _decode_uu(encoded): ++ """Decode uuencoded data.""" ++ decoded_lines = [] ++ encoded_lines_iter = iter(encoded.splitlines()) ++ for line in encoded_lines_iter: ++ if line.startswith(b"begin "): ++ mode, _, path = line.removeprefix(b"begin ").partition(b" ") ++ try: ++ int(mode, base=8) ++ except ValueError: ++ continue ++ else: ++ break ++ else: ++ raise ValueError("`begin` line not found") ++ for line in encoded_lines_iter: ++ if not line: ++ raise ValueError("Truncated input") ++ elif line.strip(b' \t\r\n\f') == b'end': ++ break ++ try: ++ decoded_line = binascii.a2b_uu(line) ++ except binascii.Error: ++ # Workaround for broken uuencoders by /Fredrik Lundh ++ nbytes = (((line[0]-32) & 63) * 4 + 5) // 3 ++ decoded_line = binascii.a2b_uu(line[:nbytes]) ++ decoded_lines.append(decoded_line) ++ ++ return b''.join(decoded_lines) ++ ++ + class Message(object): + """Basic message object. + +@@ -262,13 +292,10 @@ class Message(object): + self.policy.handle_defect(self, defect) + return value + elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): +- in_file = BytesIO(bpayload) +- out_file = BytesIO() + try: +- uu.decode(in_file, out_file, quiet=True) +- return out_file.getvalue() +- except uu.Error: +- # Some decoding problem ++ return _decode_uu(bpayload) ++ except ValueError: ++ # Some decoding problem. + return bpayload + if isinstance(payload, str): + return bpayload diff --git a/contrib/python/future/py3/patches/08-support-python-3.13.patch b/contrib/python/future/py3/patches/08-support-python-3.13.patch new file mode 100644 index 00000000000..9b726f25e98 --- /dev/null +++ b/contrib/python/future/py3/patches/08-support-python-3.13.patch @@ -0,0 +1,103 @@ +--- contrib/python/future/py3/future/backports/email/mime/audio.py (index) ++++ contrib/python/future/py3/future/backports/email/mime/audio.py (working tree) +@@ -9,37 +9,11 @@ from __future__ import absolute_import + + __all__ = ['MIMEAudio'] + +-import sndhdr +- + from io import BytesIO + from future.backports.email import encoders + from future.backports.email.mime.nonmultipart import MIMENonMultipart + + +-_sndhdr_MIMEmap = {'au' : 'basic', +- 'wav' :'x-wav', +- 'aiff':'x-aiff', +- 'aifc':'x-aiff', +- } +- +-# There are others in sndhdr that don't have MIME types. :( +-# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? +-def _whatsnd(data): +- """Try to identify a sound file type. +- +- sndhdr.what() has a pretty cruddy interface, unfortunately. This is why +- we re-do it here. It would be easier to reverse engineer the Unix 'file' +- command and use the standard 'magic' file, as shipped with a modern Unix. +- """ +- hdr = data[:512] +- fakefile = BytesIO(hdr) +- for testfn in sndhdr.tests: +- res = testfn(hdr, fakefile) +- if res is not None: +- return _sndhdr_MIMEmap.get(res[0]) +- return None +- +- + class MIMEAudio(MIMENonMultipart): + """Class for generating audio/* MIME documents.""" + +@@ -66,9 +40,61 @@ class MIMEAudio(MIMENonMultipart): + header. + """ + if _subtype is None: +- _subtype = _whatsnd(_audiodata) ++ _subtype = _what(_audiodata) + if _subtype is None: + raise TypeError('Could not find audio MIME subtype') + MIMENonMultipart.__init__(self, 'audio', _subtype, **_params) + self.set_payload(_audiodata) + _encoder(self) ++ ++ ++_rules = [] ++ ++ ++# Originally from the sndhdr module. ++# ++# There are others in sndhdr that don't have MIME types. :( ++# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? ++def _what(data): ++ # Try to identify a sound file type. ++ # ++ # sndhdr.what() had a pretty cruddy interface, unfortunately. This is why ++ # we re-do it here. It would be easier to reverse engineer the Unix 'file' ++ # command and use the standard 'magic' file, as shipped with a modern Unix. ++ for testfn in _rules: ++ if res := testfn(data): ++ return res ++ else: ++ return None ++ ++ ++def rule(rulefunc): ++ _rules.append(rulefunc) ++ return rulefunc ++ ++ ++@rule ++def _aiff(h): ++ if not h.startswith(b'FORM'): ++ return None ++ if h[8:12] in {b'AIFC', b'AIFF'}: ++ return 'x-aiff' ++ else: ++ return None ++ ++ ++@rule ++def _au(h): ++ if h.startswith(b'.snd'): ++ return 'basic' ++ else: ++ return None ++ ++ ++@rule ++def _wav(h): ++ # 'RIFF' <len> 'WAVE' 'fmt ' <len> ++ if not h.startswith(b'RIFF') or h[8:12] != b'WAVE' or h[12:16] != b'fmt ': ++ return None ++ else: ++ return "x-wav" diff --git a/contrib/python/future/py3/patches/09-support-python-3.13.patch b/contrib/python/future/py3/patches/09-support-python-3.13.patch new file mode 100644 index 00000000000..ebcd7356378 --- /dev/null +++ b/contrib/python/future/py3/patches/09-support-python-3.13.patch @@ -0,0 +1,131 @@ +--- contrib/python/future/py3/future/backports/email/mime/image.py (index) ++++ contrib/python/future/py3/future/backports/email/mime/image.py (working tree) +@@ -9,8 +9,6 @@ from __future__ import absolute_import + + __all__ = ['MIMEImage'] + +-import imghdr +- + from future.backports.email import encoders + from future.backports.email.mime.nonmultipart import MIMENonMultipart + +@@ -40,9 +38,118 @@ class MIMEImage(MIMENonMultipart): + header. + """ + if _subtype is None: +- _subtype = imghdr.what(None, _imagedata) ++ _subtype = _what(_imagedata) + if _subtype is None: + raise TypeError('Could not guess image MIME subtype') + MIMENonMultipart.__init__(self, 'image', _subtype, **_params) + self.set_payload(_imagedata) + _encoder(self) ++ ++ ++_rules = [] ++ ++ ++# Originally from the imghdr module. ++def _what(data): ++ for rule in _rules: ++ if res := rule(data): ++ return res ++ else: ++ return None ++ ++ ++def rule(rulefunc): ++ _rules.append(rulefunc) ++ return rulefunc ++ ++ ++@rule ++def _jpeg(h): ++ """JPEG data with JFIF or Exif markers; and raw JPEG""" ++ if h[6:10] in (b'JFIF', b'Exif'): ++ return 'jpeg' ++ elif h[:4] == b'\xff\xd8\xff\xdb': ++ return 'jpeg' ++ ++ ++@rule ++def _png(h): ++ if h.startswith(b'\211PNG\r\n\032\n'): ++ return 'png' ++ ++ ++@rule ++def _gif(h): ++ """GIF ('87 and '89 variants)""" ++ if h[:6] in (b'GIF87a', b'GIF89a'): ++ return 'gif' ++ ++ ++@rule ++def _tiff(h): ++ """TIFF (can be in Motorola or Intel byte order)""" ++ if h[:2] in (b'MM', b'II'): ++ return 'tiff' ++ ++ ++@rule ++def _rgb(h): ++ """SGI image library""" ++ if h.startswith(b'\001\332'): ++ return 'rgb' ++ ++ ++@rule ++def _pbm(h): ++ """PBM (portable bitmap)""" ++ if len(h) >= 3 and \ ++ h[0] == ord(b'P') and h[1] in b'14' and h[2] in b' \t\n\r': ++ return 'pbm' ++ ++ ++@rule ++def _pgm(h): ++ """PGM (portable graymap)""" ++ if len(h) >= 3 and \ ++ h[0] == ord(b'P') and h[1] in b'25' and h[2] in b' \t\n\r': ++ return 'pgm' ++ ++ ++@rule ++def _ppm(h): ++ """PPM (portable pixmap)""" ++ if len(h) >= 3 and \ ++ h[0] == ord(b'P') and h[1] in b'36' and h[2] in b' \t\n\r': ++ return 'ppm' ++ ++ ++@rule ++def _rast(h): ++ """Sun raster file""" ++ if h.startswith(b'\x59\xA6\x6A\x95'): ++ return 'rast' ++ ++ ++@rule ++def _xbm(h): ++ """X bitmap (X10 or X11)""" ++ if h.startswith(b'#define '): ++ return 'xbm' ++ ++ ++@rule ++def _bmp(h): ++ if h.startswith(b'BM'): ++ return 'bmp' ++ ++ ++@rule ++def _webp(h): ++ if h.startswith(b'RIFF') and h[8:12] == b'WEBP': ++ return 'webp' ++ ++ ++@rule ++def _exr(h): ++ if h.startswith(b'\x76\x2f\x31\x01'): ++ return 'exr' diff --git a/contrib/python/moto/py3/moto/s3/responses.py b/contrib/python/moto/py3/moto/s3/responses.py index 8784d98632d..06171e79c75 100644 --- a/contrib/python/moto/py3/moto/s3/responses.py +++ b/contrib/python/moto/py3/moto/s3/responses.py @@ -362,10 +362,7 @@ class S3Response(BaseResponse): # # Workaround - manually reverse the encoding. # Keep the + encoded, ensuring that parse_qsl doesn't replace it, and parse_qsl will unquote it afterwards - # - # YQ-1825: Replace was commented out as the version of `Werkzeug` - # that we are using is 2.0.3 (lesser than 2.1.0) and workaround is not needed - qs = (parsed_url.query or "") #.replace("+", "%2B") + qs = (parsed_url.query or "").replace("+", "%2B") return parse_qs(qs, keep_blank_values=True) def _bucket_response_head( diff --git a/contrib/python/moto/py3/patches/03-arcadia.patch b/contrib/python/moto/py3/patches/03-arcadia.patch deleted file mode 100644 index 4f01e0d0a40..00000000000 --- a/contrib/python/moto/py3/patches/03-arcadia.patch +++ /dev/null @@ -1,14 +0,0 @@ ---- contrib/python/moto/py3/moto/s3/responses.py (index) -+++ contrib/python/moto/py3/moto/s3/responses.py (working tree) -@@ -362,7 +362,10 @@ class S3Response(BaseResponse): - # - # Workaround - manually reverse the encoding. - # Keep the + encoded, ensuring that parse_qsl doesn't replace it, and parse_qsl will unquote it afterwards -- qs = (parsed_url.query or "").replace("+", "%2B") -+ # -+ # YQ-1825: Replace was commented out as the version of `Werkzeug` -+ # that we are using is 2.0.3 (lesser than 2.1.0) and workaround is not needed -+ qs = (parsed_url.query or "") #.replace("+", "%2B") - return parse_qs(qs, keep_blank_values=True) - - def _bucket_response_head( diff --git a/contrib/python/scramp/.dist-info/METADATA b/contrib/python/scramp/.dist-info/METADATA index 0058fe6943a..e3fded9df5b 100644 --- a/contrib/python/scramp/.dist-info/METADATA +++ b/contrib/python/scramp/.dist-info/METADATA @@ -1,6 +1,6 @@ -Metadata-Version: 2.3 +Metadata-Version: 2.4 Name: scramp -Version: 1.4.5 +Version: 1.4.6 Summary: An implementation of the SCRAM protocol. Project-URL: Homepage, https://github.com/tlocke/scramp Author: The Contributors @@ -409,6 +409,11 @@ Run `tox` to make sure all tests pass, then update the release notes, then do: ## Release Notes +### Version 1.4.6, 2025-07-05 + +- Drop support for Python 3.8, and add support for 3.13 + + ### Version 1.4.5, 2024-04-13 - Drop support for Python 3.7, which means we're not dependent on `importlib-metadata` anymore. diff --git a/contrib/python/scramp/README.md b/contrib/python/scramp/README.md index 43641445be5..71793c1b378 100644 --- a/contrib/python/scramp/README.md +++ b/contrib/python/scramp/README.md @@ -383,6 +383,11 @@ Run `tox` to make sure all tests pass, then update the release notes, then do: ## Release Notes +### Version 1.4.6, 2025-07-05 + +- Drop support for Python 3.8, and add support for 3.13 + + ### Version 1.4.5, 2024-04-13 - Drop support for Python 3.7, which means we're not dependent on `importlib-metadata` anymore. diff --git a/contrib/python/scramp/scramp/__init__.py b/contrib/python/scramp/scramp/__init__.py index 8bfbcd20725..4d7a7083e5e 100644 --- a/contrib/python/scramp/scramp/__init__.py +++ b/contrib/python/scramp/scramp/__init__.py @@ -7,6 +7,6 @@ from scramp.core import ( make_channel_binding, ) -__all__ = [ScramClient, ScramMechanism, ScramException, make_channel_binding] +__all__ = ["ScramClient", "ScramException", "ScramMechanism", "make_channel_binding"] __version__ = version("scramp") diff --git a/contrib/python/scramp/ya.make b/contrib/python/scramp/ya.make index 787e9aa7e5c..99e03e01548 100644 --- a/contrib/python/scramp/ya.make +++ b/contrib/python/scramp/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(1.4.5) +VERSION(1.4.6) LICENSE(MIT-0) diff --git a/contrib/python/types-protobuf/.dist-info/METADATA b/contrib/python/types-protobuf/.dist-info/METADATA index 0bf803fbc14..eed8dfd2af2 100644 --- a/contrib/python/types-protobuf/.dist-info/METADATA +++ b/contrib/python/types-protobuf/.dist-info/METADATA @@ -1,6 +1,6 @@ Metadata-Version: 2.4 Name: types-protobuf -Version: 6.30.2.20250516 +Version: 6.30.2.20250703 Summary: Typing stubs for protobuf License-Expression: Apache-2.0 Project-URL: Homepage, https://github.com/python/typeshed @@ -42,8 +42,8 @@ for more details. The source for this package can be found in the directory. This package was tested with -mypy 1.15.0, +mypy 1.16.1, pyright 1.1.400, and pytype 2024.10.11. It was generated from typeshed commit -[`126768408a69b7a3a09b7d3992970b289f92937e`](https://github.com/python/typeshed/commit/126768408a69b7a3a09b7d3992970b289f92937e). +[`dbd3ad356ef3bceae506fb71a29a48b6192213ba`](https://github.com/python/typeshed/commit/dbd3ad356ef3bceae506fb71a29a48b6192213ba). diff --git a/contrib/python/types-protobuf/README.md b/contrib/python/types-protobuf/README.md index 5b485981e77..4734a9b1ff7 100644 --- a/contrib/python/types-protobuf/README.md +++ b/contrib/python/types-protobuf/README.md @@ -25,8 +25,8 @@ for more details. The source for this package can be found in the directory. This package was tested with -mypy 1.15.0, +mypy 1.16.1, pyright 1.1.400, and pytype 2024.10.11. It was generated from typeshed commit -[`126768408a69b7a3a09b7d3992970b289f92937e`](https://github.com/python/typeshed/commit/126768408a69b7a3a09b7d3992970b289f92937e).
\ No newline at end of file +[`dbd3ad356ef3bceae506fb71a29a48b6192213ba`](https://github.com/python/typeshed/commit/dbd3ad356ef3bceae506fb71a29a48b6192213ba).
\ No newline at end of file diff --git a/contrib/python/types-protobuf/google-stubs/protobuf/internal/containers.pyi b/contrib/python/types-protobuf/google-stubs/protobuf/internal/containers.pyi index 75261371607..1431c3b1379 100644 --- a/contrib/python/types-protobuf/google-stubs/protobuf/internal/containers.pyi +++ b/contrib/python/types-protobuf/google-stubs/protobuf/internal/containers.pyi @@ -72,7 +72,9 @@ class ScalarMap(MutableMapping[_K, _ScalarV]): @overload def get(self, key: _K, default: None = None) -> _ScalarV | None: ... @overload - def get(self, key: _K, default: _ScalarV | _T) -> _ScalarV | _T: ... + def get(self, key: _K, default: _ScalarV) -> _ScalarV: ... + @overload + def get(self, key: _K, default: _T) -> _ScalarV | _T: ... def setdefault(self, key: _K, value: _ScalarV | None = None) -> _ScalarV: ... def MergeFrom(self, other: Self): ... def InvalidateIterators(self) -> None: ... @@ -95,7 +97,9 @@ class MessageMap(MutableMapping[_K, _MessageV]): @overload def get(self, key: _K, default: None = None) -> _MessageV | None: ... @overload - def get(self, key: _K, default: _MessageV | _T) -> _MessageV | _T: ... + def get(self, key: _K, default: _MessageV) -> _MessageV: ... + @overload + def get(self, key: _K, default: _T) -> _MessageV | _T: ... def get_or_create(self, key: _K) -> _MessageV: ... def setdefault(self, key: _K, value: _MessageV | None = None) -> _MessageV: ... def MergeFrom(self, other: Self): ... diff --git a/contrib/python/types-protobuf/ya.make b/contrib/python/types-protobuf/ya.make index 33f3f6ac13c..c70d8edee22 100644 --- a/contrib/python/types-protobuf/ya.make +++ b/contrib/python/types-protobuf/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(6.30.2.20250516) +VERSION(6.30.2.20250703) LICENSE(Apache-2.0) |
