aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobot-piglet <robot-piglet@yandex-team.com>2024-04-01 04:03:58 +0300
committerrobot-piglet <robot-piglet@yandex-team.com>2024-04-01 04:15:53 +0300
commit6e7835911e36724b4c14f4888d6f0222994eadf0 (patch)
tree7f6210d918014bc308e98934e896d34aadb9bac0
parent8e91894b5642604c33982805651a0f59bcdaf137 (diff)
downloadydb-6e7835911e36724b4c14f4888d6f0222994eadf0.tar.gz
Intermediate changes
-rw-r--r--contrib/python/fonttools/.dist-info/METADATA9
-rw-r--r--contrib/python/fonttools/fontTools/__init__.py2
-rw-r--r--contrib/python/fonttools/fontTools/cu2qu/ufo.py12
-rw-r--r--contrib/python/fonttools/fontTools/pens/basePen.py50
-rw-r--r--contrib/python/fonttools/fontTools/pens/filterPen.py86
-rw-r--r--contrib/python/fonttools/fontTools/pens/pointPen.py79
-rw-r--r--contrib/python/fonttools/fontTools/pens/recordingPen.py158
-rw-r--r--contrib/python/fonttools/fontTools/varLib/instancer/__init__.py6
-rw-r--r--contrib/python/fonttools/ya.make2
-rw-r--r--yt/yt/library/profiling/solomon/exporter.cpp5
-rw-r--r--yt/yt/library/profiling/solomon/exporter.h2
-rw-r--r--yt/yt/library/profiling/solomon/private.h2
-rw-r--r--yt/yt/library/profiling/solomon/producer.cpp127
-rw-r--r--yt/yt/library/profiling/solomon/producer.h4
-rw-r--r--yt/yt/library/profiling/solomon/registry.cpp5
-rw-r--r--yt/yt/library/profiling/solomon/registry.h1
16 files changed, 468 insertions, 82 deletions
diff --git a/contrib/python/fonttools/.dist-info/METADATA b/contrib/python/fonttools/.dist-info/METADATA
index a711f82b4e..b374ebc499 100644
--- a/contrib/python/fonttools/.dist-info/METADATA
+++ b/contrib/python/fonttools/.dist-info/METADATA
@@ -1,6 +1,6 @@
Metadata-Version: 2.1
Name: fonttools
-Version: 4.49.0
+Version: 4.50.0
Summary: Tools to manipulate font files
Home-page: http://github.com/fonttools/fonttools
Author: Just van Rossum
@@ -375,6 +375,13 @@ Have fun!
Changelog
~~~~~~~~~
+4.50.0 (released 2024-03-15)
+----------------------------
+
+- [pens] Added decomposing filter pens that draw components as regular contours (#3460).
+- [instancer] Drop explicit no-op axes from TupleVariations (#3457).
+- [cu2qu/ufo] Return set of modified glyph names from fonts_to_quadratic (#3456).
+
4.49.0 (released 2024-02-15)
----------------------------
diff --git a/contrib/python/fonttools/fontTools/__init__.py b/contrib/python/fonttools/fontTools/__init__.py
index e6a745bd52..ead49e26c8 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.49.0"
+version = __version__ = "4.50.0"
__all__ = ["version", "log", "configLogger"]
diff --git a/contrib/python/fonttools/fontTools/cu2qu/ufo.py b/contrib/python/fonttools/fontTools/cu2qu/ufo.py
index 10367cfecf..7a6dbc67f8 100644
--- a/contrib/python/fonttools/fontTools/cu2qu/ufo.py
+++ b/contrib/python/fonttools/fontTools/cu2qu/ufo.py
@@ -250,7 +250,7 @@ def fonts_to_quadratic(
compatibility. If this is not required, calling fonts_to_quadratic with one
font at a time may yield slightly more optimized results.
- Return True if fonts were modified, else return False.
+ Return the set of modified glyph names if any, else return an empty set.
By default, cu2qu stores the curve type in the fonts' lib, under a private
key "com.github.googlei18n.cu2qu.curve_type", and will not try to convert
@@ -296,7 +296,7 @@ def fonts_to_quadratic(
elif max_err_em:
max_errors = [f.info.unitsPerEm * max_err_em for f in fonts]
- modified = False
+ modified = set()
glyph_errors = {}
for name in set().union(*(f.keys() for f in fonts)):
glyphs = []
@@ -306,9 +306,10 @@ def fonts_to_quadratic(
glyphs.append(font[name])
cur_max_errors.append(error)
try:
- modified |= _glyphs_to_quadratic(
+ if _glyphs_to_quadratic(
glyphs, cur_max_errors, reverse_direction, stats, all_quadratic
- )
+ ):
+ modified.add(name)
except IncompatibleGlyphsError as exc:
logger.error(exc)
glyph_errors[name] = exc
@@ -329,7 +330,6 @@ def fonts_to_quadratic(
new_curve_type = "quadratic" if all_quadratic else "mixed"
if curve_type != new_curve_type:
font.lib[CURVE_TYPE_LIB_KEY] = new_curve_type
- modified = True
return modified
@@ -343,7 +343,7 @@ def glyph_to_quadratic(glyph, **kwargs):
def font_to_quadratic(font, **kwargs):
"""Convenience wrapper around fonts_to_quadratic, for just one font.
- Return True if the font was modified, else return False.
+ Return the set of modified glyph names if any, else return empty set.
"""
return fonts_to_quadratic([font], **kwargs)
diff --git a/contrib/python/fonttools/fontTools/pens/basePen.py b/contrib/python/fonttools/fontTools/pens/basePen.py
index 5d2cf5032c..ba38f70090 100644
--- a/contrib/python/fonttools/fontTools/pens/basePen.py
+++ b/contrib/python/fonttools/fontTools/pens/basePen.py
@@ -39,7 +39,7 @@ sequence of length 2 will do.
from typing import Tuple, Dict
from fontTools.misc.loggingTools import LogMixin
-from fontTools.misc.transform import DecomposedTransform
+from fontTools.misc.transform import DecomposedTransform, Identity
__all__ = [
"AbstractPen",
@@ -195,17 +195,40 @@ class DecomposingPen(LoggingPen):
By default a warning message is logged when a base glyph is missing;
set the class variable ``skipMissingComponents`` to False if you want
- to raise a :class:`MissingComponentError` exception.
+ all instances of a sub-class to raise a :class:`MissingComponentError`
+ exception by default.
"""
skipMissingComponents = True
+ # alias error for convenience
+ MissingComponentError = MissingComponentError
- def __init__(self, glyphSet):
- """Takes a single 'glyphSet' argument (dict), in which the glyphs
- that are referenced as components are looked up by their name.
+ def __init__(
+ self,
+ glyphSet,
+ *args,
+ skipMissingComponents=None,
+ reverseFlipped=False,
+ **kwargs,
+ ):
+ """Takes a 'glyphSet' argument (dict), in which the glyphs that are referenced
+ as components are looked up by their name.
+
+ If the optional 'reverseFlipped' argument is True, components whose transformation
+ matrix has a negative determinant will be decomposed with a reversed path direction
+ to compensate for the flip.
+
+ The optional 'skipMissingComponents' argument can be set to True/False to
+ override the homonymous class attribute for a given pen instance.
"""
- super(DecomposingPen, self).__init__()
+ super(DecomposingPen, self).__init__(*args, **kwargs)
self.glyphSet = glyphSet
+ self.skipMissingComponents = (
+ self.__class__.skipMissingComponents
+ if skipMissingComponents is None
+ else skipMissingComponents
+ )
+ self.reverseFlipped = reverseFlipped
def addComponent(self, glyphName, transformation):
"""Transform the points of the base glyph and draw it onto self."""
@@ -218,8 +241,19 @@ class DecomposingPen(LoggingPen):
raise MissingComponentError(glyphName)
self.log.warning("glyph '%s' is missing from glyphSet; skipped" % glyphName)
else:
- tPen = TransformPen(self, transformation)
- glyph.draw(tPen)
+ pen = self
+ if transformation != Identity:
+ pen = TransformPen(pen, transformation)
+ if self.reverseFlipped:
+ # if the transformation has a negative determinant, it will
+ # reverse the contour direction of the component
+ a, b, c, d = transformation[:4]
+ det = a * d - b * c
+ if det < 0:
+ from fontTools.pens.reverseContourPen import ReverseContourPen
+
+ pen = ReverseContourPen(pen)
+ glyph.draw(pen)
def addVarComponent(self, glyphName, transformation, location):
# GlyphSet decomposes for us
diff --git a/contrib/python/fonttools/fontTools/pens/filterPen.py b/contrib/python/fonttools/fontTools/pens/filterPen.py
index 6c8712c261..f104e67dd3 100644
--- a/contrib/python/fonttools/fontTools/pens/filterPen.py
+++ b/contrib/python/fonttools/fontTools/pens/filterPen.py
@@ -1,5 +1,7 @@
-from fontTools.pens.basePen import AbstractPen
-from fontTools.pens.pointPen import AbstractPointPen
+from __future__ import annotations
+
+from fontTools.pens.basePen import AbstractPen, DecomposingPen
+from fontTools.pens.pointPen import AbstractPointPen, DecomposingPointPen
from fontTools.pens.recordingPen import RecordingPen
@@ -150,8 +152,8 @@ class FilterPointPen(_PassThruComponentsMixin, AbstractPointPen):
('endPath', (), {})
"""
- def __init__(self, outPointPen):
- self._outPen = outPointPen
+ def __init__(self, outPen):
+ self._outPen = outPen
def beginPath(self, **kwargs):
self._outPen.beginPath(**kwargs)
@@ -161,3 +163,79 @@ class FilterPointPen(_PassThruComponentsMixin, AbstractPointPen):
def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)
+
+
+class _DecomposingFilterPenMixin:
+ """Mixin class that decomposes components as regular contours.
+
+ Shared by both DecomposingFilterPen and DecomposingFilterPointPen.
+
+ Takes two required parameters, another (segment or point) pen 'outPen' to draw
+ with, and a 'glyphSet' dict of drawable glyph objects to draw components from.
+
+ The 'skipMissingComponents' and 'reverseFlipped' optional arguments work the
+ same as in the DecomposingPen/DecomposingPointPen. Both are False by default.
+
+ In addition, the decomposing filter pens also take the following two options:
+
+ 'include' is an optional set of component base glyph names to consider for
+ decomposition; the default include=None means decompose all components no matter
+ the base glyph name).
+
+ 'decomposeNested' (bool) controls whether to recurse decomposition into nested
+ components of components (this only matters when 'include' was also provided);
+ if False, only decompose top-level components included in the set, but not
+ also their children.
+ """
+
+ # raises MissingComponentError if base glyph is not found in glyphSet
+ skipMissingComponents = False
+
+ def __init__(
+ self,
+ outPen,
+ glyphSet,
+ skipMissingComponents=None,
+ reverseFlipped=False,
+ include: set[str] | None = None,
+ decomposeNested: bool = True,
+ ):
+ super().__init__(
+ outPen=outPen,
+ glyphSet=glyphSet,
+ skipMissingComponents=skipMissingComponents,
+ reverseFlipped=reverseFlipped,
+ )
+ self.include = include
+ self.decomposeNested = decomposeNested
+
+ def addComponent(self, baseGlyphName, transformation, **kwargs):
+ # only decompose the component if it's included in the set
+ if self.include is None or baseGlyphName in self.include:
+ # if we're decomposing nested components, temporarily set include to None
+ include_bak = self.include
+ if self.decomposeNested and self.include:
+ self.include = None
+ try:
+ super().addComponent(baseGlyphName, transformation, **kwargs)
+ finally:
+ if self.include != include_bak:
+ self.include = include_bak
+ else:
+ _PassThruComponentsMixin.addComponent(
+ self, baseGlyphName, transformation, **kwargs
+ )
+
+
+class DecomposingFilterPen(_DecomposingFilterPenMixin, DecomposingPen, FilterPen):
+ """Filter pen that draws components as regular contours."""
+
+ pass
+
+
+class DecomposingFilterPointPen(
+ _DecomposingFilterPenMixin, DecomposingPointPen, FilterPointPen
+):
+ """Filter point pen that draws components as regular contours."""
+
+ pass
diff --git a/contrib/python/fonttools/fontTools/pens/pointPen.py b/contrib/python/fonttools/fontTools/pens/pointPen.py
index eb1ebc2048..93a9201c99 100644
--- a/contrib/python/fonttools/fontTools/pens/pointPen.py
+++ b/contrib/python/fonttools/fontTools/pens/pointPen.py
@@ -15,8 +15,9 @@ For instance, whether or not a point is smooth, and its name.
import math
from typing import Any, Optional, Tuple, Dict
-from fontTools.pens.basePen import AbstractPen, PenError
-from fontTools.misc.transform import DecomposedTransform
+from fontTools.misc.loggingTools import LogMixin
+from fontTools.pens.basePen import AbstractPen, MissingComponentError, PenError
+from fontTools.misc.transform import DecomposedTransform, Identity
__all__ = [
"AbstractPointPen",
@@ -523,3 +524,77 @@ class ReverseContourPointPen(AbstractPointPen):
if self.currentContour is not None:
raise PenError("Components must be added before or after contours")
self.pen.addComponent(glyphName, transform, identifier=identifier, **kwargs)
+
+
+class DecomposingPointPen(LogMixin, AbstractPointPen):
+ """Implements a 'addComponent' method that decomposes components
+ (i.e. draws them onto self as simple contours).
+ It can also be used as a mixin class (e.g. see DecomposingRecordingPointPen).
+
+ You must override beginPath, addPoint, endPath. You may
+ additionally override addVarComponent and addComponent.
+
+ By default a warning message is logged when a base glyph is missing;
+ set the class variable ``skipMissingComponents`` to False if you want
+ all instances of a sub-class to raise a :class:`MissingComponentError`
+ exception by default.
+ """
+
+ skipMissingComponents = True
+ # alias error for convenience
+ MissingComponentError = MissingComponentError
+
+ def __init__(
+ self,
+ glyphSet,
+ *args,
+ skipMissingComponents=None,
+ reverseFlipped=False,
+ **kwargs,
+ ):
+ """Takes a 'glyphSet' argument (dict), in which the glyphs that are referenced
+ as components are looked up by their name.
+
+ If the optional 'reverseFlipped' argument is True, components whose transformation
+ matrix has a negative determinant will be decomposed with a reversed path direction
+ to compensate for the flip.
+
+ The optional 'skipMissingComponents' argument can be set to True/False to
+ override the homonymous class attribute for a given pen instance.
+ """
+ super().__init__(*args, **kwargs)
+ self.glyphSet = glyphSet
+ self.skipMissingComponents = (
+ self.__class__.skipMissingComponents
+ if skipMissingComponents is None
+ else skipMissingComponents
+ )
+ self.reverseFlipped = reverseFlipped
+
+ def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
+ """Transform the points of the base glyph and draw it onto self.
+
+ The `identifier` parameter and any extra kwargs are ignored.
+ """
+ from fontTools.pens.transformPen import TransformPointPen
+
+ try:
+ glyph = self.glyphSet[baseGlyphName]
+ except KeyError:
+ if not self.skipMissingComponents:
+ raise MissingComponentError(baseGlyphName)
+ self.log.warning(
+ "glyph '%s' is missing from glyphSet; skipped" % baseGlyphName
+ )
+ else:
+ pen = self
+ if transformation != Identity:
+ pen = TransformPointPen(pen, transformation)
+ if self.reverseFlipped:
+ # if the transformation has a negative determinant, it will
+ # reverse the contour direction of the component
+ a, b, c, d = transformation[:4]
+ det = a * d - b * c
+ if a * d - b * c < 0:
+ pen = ReverseContourPointPen(pen)
+ glyph.drawPoints(pen)
diff --git a/contrib/python/fonttools/fontTools/pens/recordingPen.py b/contrib/python/fonttools/fontTools/pens/recordingPen.py
index 4f44a4d59f..ba165e1951 100644
--- a/contrib/python/fonttools/fontTools/pens/recordingPen.py
+++ b/contrib/python/fonttools/fontTools/pens/recordingPen.py
@@ -1,13 +1,14 @@
"""Pen recording operations that can be accessed or replayed."""
from fontTools.pens.basePen import AbstractPen, DecomposingPen
-from fontTools.pens.pointPen import AbstractPointPen
+from fontTools.pens.pointPen import AbstractPointPen, DecomposingPointPen
__all__ = [
"replayRecording",
"RecordingPen",
"DecomposingRecordingPen",
+ "DecomposingRecordingPointPen",
"RecordingPointPen",
"lerpRecordings",
]
@@ -85,28 +86,55 @@ class DecomposingRecordingPen(DecomposingPen, RecordingPen):
"""Same as RecordingPen, except that it doesn't keep components
as references, but draws them decomposed as regular contours.
- The constructor takes a single 'glyphSet' positional argument,
+ The constructor takes a required 'glyphSet' positional argument,
a dictionary of glyph objects (i.e. with a 'draw' method) keyed
- by thir name::
-
- >>> class SimpleGlyph(object):
- ... def draw(self, pen):
- ... pen.moveTo((0, 0))
- ... pen.curveTo((1, 1), (2, 2), (3, 3))
- ... pen.closePath()
- >>> class CompositeGlyph(object):
- ... def draw(self, pen):
- ... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
- >>> glyphSet = {'a': SimpleGlyph(), 'b': CompositeGlyph()}
- >>> for name, glyph in sorted(glyphSet.items()):
- ... pen = DecomposingRecordingPen(glyphSet)
- ... glyph.draw(pen)
- ... print("{}: {}".format(name, pen.value))
- a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
- b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
+ by thir name; other arguments are forwarded to the DecomposingPen's
+ constructor::
+
+ >>> class SimpleGlyph(object):
+ ... def draw(self, pen):
+ ... pen.moveTo((0, 0))
+ ... pen.curveTo((1, 1), (2, 2), (3, 3))
+ ... pen.closePath()
+ >>> class CompositeGlyph(object):
+ ... def draw(self, pen):
+ ... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
+ >>> class MissingComponent(object):
+ ... def draw(self, pen):
+ ... pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
+ >>> class FlippedComponent(object):
+ ... def draw(self, pen):
+ ... pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
+ >>> glyphSet = {
+ ... 'a': SimpleGlyph(),
+ ... 'b': CompositeGlyph(),
+ ... 'c': MissingComponent(),
+ ... 'd': FlippedComponent(),
+ ... }
+ >>> for name, glyph in sorted(glyphSet.items()):
+ ... pen = DecomposingRecordingPen(glyphSet)
+ ... try:
+ ... glyph.draw(pen)
+ ... except pen.MissingComponentError:
+ ... pass
+ ... print("{}: {}".format(name, pen.value))
+ a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
+ b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
+ c: []
+ d: [('moveTo', ((0, 0),)), ('curveTo', ((-1, 1), (-2, 2), (-3, 3))), ('closePath', ())]
+ >>> for name, glyph in sorted(glyphSet.items()):
+ ... pen = DecomposingRecordingPen(
+ ... glyphSet, skipMissingComponents=True, reverseFlipped=True,
+ ... )
+ ... glyph.draw(pen)
+ ... print("{}: {}".format(name, pen.value))
+ a: [('moveTo', ((0, 0),)), ('curveTo', ((1, 1), (2, 2), (3, 3))), ('closePath', ())]
+ b: [('moveTo', ((-1, 1),)), ('curveTo', ((0, 2), (1, 3), (2, 4))), ('closePath', ())]
+ c: []
+ d: [('moveTo', ((0, 0),)), ('lineTo', ((-3, 3),)), ('curveTo', ((-2, 2), (-1, 1), (0, 0))), ('closePath', ())]
"""
- # raises KeyError if base glyph is not found in glyphSet
+ # raises MissingComponentError(KeyError) if base glyph is not found in glyphSet
skipMissingComponents = False
@@ -174,6 +202,96 @@ class RecordingPointPen(AbstractPointPen):
drawPoints = replay
+class DecomposingRecordingPointPen(DecomposingPointPen, RecordingPointPen):
+ """Same as RecordingPointPen, except that it doesn't keep components
+ as references, but draws them decomposed as regular contours.
+
+ The constructor takes a required 'glyphSet' positional argument,
+ a dictionary of pointPen-drawable glyph objects (i.e. with a 'drawPoints' method)
+ keyed by thir name; other arguments are forwarded to the DecomposingPointPen's
+ constructor::
+
+ >>> from pprint import pprint
+ >>> class SimpleGlyph(object):
+ ... def drawPoints(self, pen):
+ ... pen.beginPath()
+ ... pen.addPoint((0, 0), "line")
+ ... pen.addPoint((1, 1))
+ ... pen.addPoint((2, 2))
+ ... pen.addPoint((3, 3), "curve")
+ ... pen.endPath()
+ >>> class CompositeGlyph(object):
+ ... def drawPoints(self, pen):
+ ... pen.addComponent('a', (1, 0, 0, 1, -1, 1))
+ >>> class MissingComponent(object):
+ ... def drawPoints(self, pen):
+ ... pen.addComponent('foobar', (1, 0, 0, 1, 0, 0))
+ >>> class FlippedComponent(object):
+ ... def drawPoints(self, pen):
+ ... pen.addComponent('a', (-1, 0, 0, 1, 0, 0))
+ >>> glyphSet = {
+ ... 'a': SimpleGlyph(),
+ ... 'b': CompositeGlyph(),
+ ... 'c': MissingComponent(),
+ ... 'd': FlippedComponent(),
+ ... }
+ >>> for name, glyph in sorted(glyphSet.items()):
+ ... pen = DecomposingRecordingPointPen(glyphSet)
+ ... try:
+ ... glyph.drawPoints(pen)
+ ... except pen.MissingComponentError:
+ ... pass
+ ... pprint({name: pen.value})
+ {'a': [('beginPath', (), {}),
+ ('addPoint', ((0, 0), 'line', False, None), {}),
+ ('addPoint', ((1, 1), None, False, None), {}),
+ ('addPoint', ((2, 2), None, False, None), {}),
+ ('addPoint', ((3, 3), 'curve', False, None), {}),
+ ('endPath', (), {})]}
+ {'b': [('beginPath', (), {}),
+ ('addPoint', ((-1, 1), 'line', False, None), {}),
+ ('addPoint', ((0, 2), None, False, None), {}),
+ ('addPoint', ((1, 3), None, False, None), {}),
+ ('addPoint', ((2, 4), 'curve', False, None), {}),
+ ('endPath', (), {})]}
+ {'c': []}
+ {'d': [('beginPath', (), {}),
+ ('addPoint', ((0, 0), 'line', False, None), {}),
+ ('addPoint', ((-1, 1), None, False, None), {}),
+ ('addPoint', ((-2, 2), None, False, None), {}),
+ ('addPoint', ((-3, 3), 'curve', False, None), {}),
+ ('endPath', (), {})]}
+ >>> for name, glyph in sorted(glyphSet.items()):
+ ... pen = DecomposingRecordingPointPen(
+ ... glyphSet, skipMissingComponents=True, reverseFlipped=True,
+ ... )
+ ... glyph.drawPoints(pen)
+ ... pprint({name: pen.value})
+ {'a': [('beginPath', (), {}),
+ ('addPoint', ((0, 0), 'line', False, None), {}),
+ ('addPoint', ((1, 1), None, False, None), {}),
+ ('addPoint', ((2, 2), None, False, None), {}),
+ ('addPoint', ((3, 3), 'curve', False, None), {}),
+ ('endPath', (), {})]}
+ {'b': [('beginPath', (), {}),
+ ('addPoint', ((-1, 1), 'line', False, None), {}),
+ ('addPoint', ((0, 2), None, False, None), {}),
+ ('addPoint', ((1, 3), None, False, None), {}),
+ ('addPoint', ((2, 4), 'curve', False, None), {}),
+ ('endPath', (), {})]}
+ {'c': []}
+ {'d': [('beginPath', (), {}),
+ ('addPoint', ((0, 0), 'curve', False, None), {}),
+ ('addPoint', ((-3, 3), 'line', False, None), {}),
+ ('addPoint', ((-2, 2), None, False, None), {}),
+ ('addPoint', ((-1, 1), None, False, None), {}),
+ ('endPath', (), {})]}
+ """
+
+ # raises MissingComponentError(KeyError) if base glyph is not found in glyphSet
+ skipMissingComponents = False
+
+
def lerpRecordings(recording1, recording2, factor=0.5):
"""Linearly interpolate between two recordings. The recordings
must be decomposed, i.e. they must not contain any components.
diff --git a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py
index 89427dc534..c5de81cad0 100644
--- a/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py
+++ b/contrib/python/fonttools/fontTools/varLib/instancer/__init__.py
@@ -531,9 +531,13 @@ def changeTupleVariationsAxisLimits(variations, axisLimits):
def changeTupleVariationAxisLimit(var, axisTag, axisLimit):
assert isinstance(axisLimit, NormalizedAxisTripleAndDistances)
- # Skip when current axis is missing (i.e. doesn't participate),
+ # Skip when current axis is missing or peaks at 0 (i.e. doesn't participate)
lower, peak, upper = var.axes.get(axisTag, (-1, 0, 1))
if peak == 0:
+ # explicitly defined, no-op axes can be omitted
+ # https://github.com/fonttools/fonttools/issues/3453
+ if axisTag in var.axes:
+ del var.axes[axisTag]
return [var]
# Drop if the var 'tent' isn't well-formed
if not (lower <= peak <= upper) or (lower < 0 and upper > 0):
diff --git a/contrib/python/fonttools/ya.make b/contrib/python/fonttools/ya.make
index e5986f9d03..a6872287a9 100644
--- a/contrib/python/fonttools/ya.make
+++ b/contrib/python/fonttools/ya.make
@@ -2,7 +2,7 @@
PY3_LIBRARY()
-VERSION(4.49.0)
+VERSION(4.50.0)
LICENSE(MIT)
diff --git a/yt/yt/library/profiling/solomon/exporter.cpp b/yt/yt/library/profiling/solomon/exporter.cpp
index 6cbb80632d..a7d2a356ec 100644
--- a/yt/yt/library/profiling/solomon/exporter.cpp
+++ b/yt/yt/library/profiling/solomon/exporter.cpp
@@ -118,6 +118,10 @@ void TSolomonExporterConfig::Register(TRegistrar registrar)
registrar.Parameter("update_sensor_service_tree_period", &TThis::UpdateSensorServiceTreePeriod)
.Default(TDuration::Seconds(30));
+ registrar.Parameter("producer_collection_batch_size", &TThis::ProducerCollectionBatchSize)
+ .Default(DefaultProducerCollectionBatchSize)
+ .GreaterThan(0);
+
registrar.Postprocessor([] (TThis* config) {
if (config->LingerTimeout.GetValue() % config->GridStep.GetValue() != 0) {
THROW_ERROR_EXCEPTION("\"linger_timeout\" must be multiple of \"grid_step\"");
@@ -208,6 +212,7 @@ TSolomonExporter::TSolomonExporter(
}
Registry_->SetWindowSize(Config_->WindowSize);
+ Registry_->SetProducerCollectionBatchSize(Config_->ProducerCollectionBatchSize);
Registry_->SetGridFactor([config = Config_] (const TString& name) -> int {
auto shard = config->MatchShard(name);
if (!shard) {
diff --git a/yt/yt/library/profiling/solomon/exporter.h b/yt/yt/library/profiling/solomon/exporter.h
index 8aad2abcb1..1f2efdbd71 100644
--- a/yt/yt/library/profiling/solomon/exporter.h
+++ b/yt/yt/library/profiling/solomon/exporter.h
@@ -84,6 +84,8 @@ struct TSolomonExporterConfig
TDuration UpdateSensorServiceTreePeriod;
+ int ProducerCollectionBatchSize;
+
TShardConfigPtr MatchShard(const TString& sensorName);
ESummaryPolicy GetSummaryPolicy() const;
diff --git a/yt/yt/library/profiling/solomon/private.h b/yt/yt/library/profiling/solomon/private.h
index f011a0e471..82bb3c6b3b 100644
--- a/yt/yt/library/profiling/solomon/private.h
+++ b/yt/yt/library/profiling/solomon/private.h
@@ -8,6 +8,8 @@ namespace NYT::NProfiling {
inline const NLogging::TLogger SolomonLogger("Solomon");
+inline const int DefaultProducerCollectionBatchSize = 100;
+
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT::NProfiling
diff --git a/yt/yt/library/profiling/solomon/producer.cpp b/yt/yt/library/profiling/solomon/producer.cpp
index e54ed9e6ce..05072800b9 100644
--- a/yt/yt/library/profiling/solomon/producer.cpp
+++ b/yt/yt/library/profiling/solomon/producer.cpp
@@ -143,6 +143,58 @@ void TCounterWriter::AddCounter(const TString& name, i64 value)
////////////////////////////////////////////////////////////////////////////////
+struct TOwningProducer
+{
+ ISensorProducerPtr Owner;
+ TProducerStatePtr Producer;
+};
+
+void DoCollectBatch(
+ const IRegistryImplPtr& profiler,
+ std::vector<TOwningProducer>&& batchArg,
+ const TEventTimer& collectDuration)
+{
+ auto batch = std::move(batchArg);
+ for (const auto& item : batch) {
+ TEventTimerGuard guard(collectDuration);
+ try {
+ const auto& producer = item.Producer;
+ const auto& buffer = item.Owner->GetBuffer();
+ if (buffer) {
+ auto lastBuffer = producer->LastBuffer.Lock();
+ if (lastBuffer == buffer) {
+ continue;
+ }
+
+ TCounterWriter writer(profiler, producer->Counters, ++producer->LastUpdateIteration);
+ buffer->WriteTo(&writer);
+ producer->LastBuffer = buffer;
+ if (producer->Counters->Options.ProducerRemoveSupport) {
+ producer->Counters->ClearOutdated(producer->LastUpdateIteration);
+ }
+ } else {
+ producer->Counters->Counters.clear();
+ producer->Counters->Gauges.clear();
+ producer->Counters->Tags.clear();
+ }
+ } catch (const std::exception& ex) {
+ YT_LOG_ERROR(ex, "Producer read failed");
+ continue;
+ }
+ }
+}
+
+TFuture<void> CollectBatchAsync(
+ const IInvokerPtr& invoker,
+ const IRegistryImplPtr& profiler,
+ std::vector<TOwningProducer>&& batch,
+ const TEventTimer& collectDuration)
+{
+ return BIND(&DoCollectBatch, profiler, Passed(std::move(batch)), collectDuration)
+ .AsyncVia(invoker)
+ .Run();
+}
+
void TProducerSet::AddProducer(TProducerStatePtr state)
{
Producers_.insert(std::move(state));
@@ -152,57 +204,51 @@ void TProducerSet::Collect(IRegistryImplPtr profiler, IInvokerPtr invoker)
{
std::vector<TFuture<void>> offloadFutures;
std::deque<TProducerStatePtr> toRemove;
+
+ std::vector<TOwningProducer> batch;
+ batch.reserve(BatchSize_);
for (const auto& producer : Producers_) {
auto owner = producer->Producer.Lock();
if (!owner) {
toRemove.push_back(producer);
- continue;
+ } else {
+ auto item = TOwningProducer{
+ .Owner = std::move(owner),
+ .Producer = producer,
+ };
+ batch.push_back(std::move(item));
}
- auto future = BIND([profiler, owner, producer, collectDuration = ProducerCollectDuration_] () {
- auto startTime = TInstant::Now();
- auto reportTime = Finally([&] {
- collectDuration.Record(TInstant::Now() - startTime);
- });
-
- try {
- auto buffer = owner->GetBuffer();
- if (buffer) {
- auto lastBuffer = producer->LastBuffer.Lock();
- if (lastBuffer == buffer) {
- return;
- }
-
- TCounterWriter writer(profiler, producer->Counters, ++producer->LastUpdateIteration);
- buffer->WriteTo(&writer);
- producer->LastBuffer = buffer;
- if (producer->Counters->Options.ProducerRemoveSupport) {
- producer->Counters->ClearOutdated(producer->LastUpdateIteration);
- }
- } else {
- producer->Counters->Counters.clear();
- producer->Counters->Gauges.clear();
- producer->Counters->Tags.clear();
- }
- } catch (const std::exception& ex) {
- YT_LOG_ERROR(ex, "Producer read failed");
- return;
- }
- })
- .AsyncVia(invoker)
- .Run();
+ if (std::ssize(batch) == BatchSize_) {
+ offloadFutures.push_back(
+ CollectBatchAsync(
+ invoker,
+ profiler,
+ std::move(batch),
+ ProducerCollectDuration_));
+ batch.clear();
+ }
+ }
+ if (!batch.empty()) {
+ offloadFutures.push_back(
+ CollectBatchAsync(
+ invoker,
+ profiler,
+ std::move(batch),
+ ProducerCollectDuration_));
+ }
+
- offloadFutures.push_back(future);
+ for (const auto& producer : toRemove) {
+ Producers_.erase(producer);
}
+ invoker->Invoke(BIND_NO_PROPAGATE([_ = std::move(toRemove)] { }));
+
// Use blocking Get(), because we want to lock current thread while data structure is updating.
for (const auto& future : offloadFutures) {
future.Get();
}
-
- for (const auto& producer : toRemove) {
- Producers_.erase(producer);
- }
}
void TProducerSet::Profile(const TProfiler& profiler)
@@ -211,6 +257,11 @@ void TProducerSet::Profile(const TProfiler& profiler)
ProducerCollectDuration_ = profiler.Timer("/producer_collect_duration");
}
+void TProducerSet::SetCollectionBatchSize(int batchSize)
+{
+ BatchSize_ = batchSize;
+}
+
////////////////////////////////////////////////////////////////////////////////
} // namespace NYT::NProfiling
diff --git a/yt/yt/library/profiling/solomon/producer.h b/yt/yt/library/profiling/solomon/producer.h
index 188309d68e..55a4c2cdf7 100644
--- a/yt/yt/library/profiling/solomon/producer.h
+++ b/yt/yt/library/profiling/solomon/producer.h
@@ -84,11 +84,15 @@ public:
void Profile(const TProfiler& profiler);
+ void SetCollectionBatchSize(int batchSize);
+
private:
THashSet<TProducerStatePtr> Producers_;
TProfiler SelfProfiler_;
TEventTimer ProducerCollectDuration_;
+
+ int BatchSize_ = DefaultProducerCollectionBatchSize;
};
////////////////////////////////////////////////////////////////////////////////
diff --git a/yt/yt/library/profiling/solomon/registry.cpp b/yt/yt/library/profiling/solomon/registry.cpp
index b231a64782..18c8ba61cf 100644
--- a/yt/yt/library/profiling/solomon/registry.cpp
+++ b/yt/yt/library/profiling/solomon/registry.cpp
@@ -276,6 +276,11 @@ void TSolomonRegistry::SetWindowSize(int windowSize)
WindowSize_ = windowSize;
}
+void TSolomonRegistry::SetProducerCollectionBatchSize(int batchSize)
+{
+ Producers_.SetCollectionBatchSize(batchSize);
+}
+
int TSolomonRegistry::GetWindowSize() const
{
if (!WindowSize_) {
diff --git a/yt/yt/library/profiling/solomon/registry.h b/yt/yt/library/profiling/solomon/registry.h
index 0569f18cd9..5b8174db02 100644
--- a/yt/yt/library/profiling/solomon/registry.h
+++ b/yt/yt/library/profiling/solomon/registry.h
@@ -128,6 +128,7 @@ public:
void SetGridFactor(std::function<int(const TString&)> gridFactor);
void SetWindowSize(int windowSize);
+ void SetProducerCollectionBatchSize(int batchSize);
void ProcessRegistrations();
void Collect(IInvokerPtr offloadInvoker = GetSyncInvoker());
void ReadSensors(