From 7fd688f5f09c8d908dc0448ccb4ca4df482dcc63 Mon Sep 17 00:00:00 2001 From: knutnergaard Date: Sun, 16 Mar 2025 13:18:01 +0100 Subject: [PATCH 1/8] Annotate `fontshell`` modules. Fix related inconsistencies in base classes. --- Lib/fontParts/base/font.py | 12 +- Lib/fontParts/base/glyph.py | 22 ++- Lib/fontParts/base/guideline.py | 4 +- Lib/fontParts/fontshell/anchor.py | 79 +++++---- Lib/fontParts/fontshell/base.py | 29 +-- Lib/fontParts/fontshell/component.py | 42 +++-- Lib/fontParts/fontshell/contour.py | 103 ++++++----- Lib/fontParts/fontshell/features.py | 19 +- Lib/fontParts/fontshell/font.py | 160 ++++++++++------- Lib/fontParts/fontshell/glyph.py | 256 ++++++++++++++++----------- Lib/fontParts/fontshell/groups.py | 63 ++++--- Lib/fontParts/fontshell/guideline.py | 84 +++++---- Lib/fontParts/fontshell/image.py | 50 ++++-- 13 files changed, 565 insertions(+), 358 deletions(-) diff --git a/Lib/fontParts/base/font.py b/Lib/fontParts/base/font.py index 75002d68..cc5d7311 100644 --- a/Lib/fontParts/base/font.py +++ b/Lib/fontParts/base/font.py @@ -295,7 +295,7 @@ def _save( self, path: Optional[str], showProgress: bool, - formatVersion: Optional[float], + formatVersion: Optional[int], fileStructure: Optional[str], **kwargs: Any, ) -> None: @@ -883,7 +883,8 @@ def _get_base_layers(self) -> Tuple[BaseLayer, ...]: self._setFontInLayer(layer) return tuple(layers) - def _get_layers(self, **kwargs: Any) -> Tuple[BaseLayer, ...]: # type: ignore[return] + # type: ignore[return] + def _get_layers(self, **kwargs: Any) -> Tuple[BaseLayer, ...]: r"""Get the native font's layer objects. This is the environment implementation of @@ -1754,7 +1755,8 @@ def _getitem__guidelines(self, index: int) -> BaseGuideline: self._setFontInGuideline(guideline) return guideline - def _getGuideline(self, index: int, **kwargs: Any) -> BaseGuideline: # type: ignore[return] + # type: ignore[return] + def _getGuideline(self, index: int, **kwargs: Any) -> BaseGuideline: r"""Return the guideline at the given index. :param index: The index of the guideline. @@ -1848,11 +1850,11 @@ def appendGuideline( def _appendGuideline( # type: ignore[return] self, - position: Optional[PairCollectionType[IntFloatType]], + position: PairCollectionType[IntFloatType], angle: Optional[float], name: Optional[str], color: Optional[QuadrupleCollectionType[IntFloatType]], - **kwargs, + **kwargs: Any ) -> BaseGuideline: r"""Append a new guideline to the native font. diff --git a/Lib/fontParts/base/glyph.py b/Lib/fontParts/base/glyph.py index dc6c9a81..44696d45 100644 --- a/Lib/fontParts/base/glyph.py +++ b/Lib/fontParts/base/glyph.py @@ -310,7 +310,7 @@ def _set_base_unicodes(self, value: CollectionType[int]) -> None: value = normalizers.normalizeGlyphUnicodes(value) self._set_unicodes(value) - def _get_unicodes(self) -> Tuple[int, ...]: # type: ignore[return] + def _get_unicodes(self) -> CollectionType[int]: # type: ignore[return] """Get the Unicode values assigned to the glyph. This is the environment implementation of @@ -1218,7 +1218,8 @@ def __getitem__(self, index: int) -> BaseContour: self._setGlyphInContour(contour) return contour - def _getContour(self, index: int, **kwargs: Any) -> BaseContour: # type: ignore[return] + # type: ignore[return] + def _getContour(self, index: int, **kwargs: Any) -> BaseContour: r"""Get the contour located at the given index from the native glyph. :param index: The index of the contour to return as an :class:`int`. @@ -1454,7 +1455,8 @@ def _getitem__components(self, index: int) -> BaseComponent: self._setGlyphInComponent(component) return component - def _getComponent(self, index: int, **kwargs: Any) -> BaseComponent: # type: ignore[return] + # type: ignore[return] + def _getComponent(self, index: int, **kwargs: Any) -> BaseComponent: r"""Get the component at the given index from the native glyph. :param index: The index of the component to return as an :class:`int`. @@ -1730,7 +1732,8 @@ def _getitem__anchors(self, index: int) -> BaseAnchor: self._setGlyphInAnchor(anchor) return anchor - def _getAnchor(self, index: int, **kwargs: Any) -> BaseAnchor: # type: ignore[return] + # type: ignore[return] + def _getAnchor(self, index: int, **kwargs: Any) -> BaseAnchor: r"""Get the anchor at the given index from the native glyph. :param index: The index of the anchor to get as an :class:`int`. @@ -1976,7 +1979,8 @@ def _getitem__guidelines(self, index: int) -> BaseGuideline: self._setGlyphInGuideline(guideline) return guideline - def _getGuideline(self, index: int, **kwargs: Any) -> BaseGuideline: # type: ignore[return] + # type: ignore[return] + def _getGuideline(self, index: int, **kwargs: Any) -> BaseGuideline: r"""Get the anchor at the given index from the native glyph. :param index: The index of the guideline to get as an :class:`int`. @@ -3241,10 +3245,11 @@ def _get_base_image(self) -> BaseImage: image.glyph = self return image - def _get_image(self) -> BaseImage: # type: ignore[return] + def _get_image(self) -> Optional[BaseImage]: # type: ignore[return] """Get the image for the native glyph. - :return: The :class:`BaseImage` subclass instance belonging to the glyph. + :return: The :class:`BaseImage` subclass instance belonging to the glyph, + or :obj:`None` if the glyph has no image. :raises NotImplementedError: If the method has not been overridden by a subclass. @@ -3415,7 +3420,8 @@ def _set_base_markColor( value = normalizers.normalizeColor(value) self._set_markColor(value) - def _get_markColor(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: # type: ignore[return] + # type: ignore[return] + def _get_markColor(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: """Get the glyph's mark color. This is the environment implementation of diff --git a/Lib/fontParts/base/guideline.py b/Lib/fontParts/base/guideline.py index f0231a65..127df77d 100644 --- a/Lib/fontParts/base/guideline.py +++ b/Lib/fontParts/base/guideline.py @@ -523,7 +523,7 @@ def _set_name(self, value: Optional[str]) -> None: """, ) - def _get_base_color(self) -> QuadrupleType[float]: + def _get_base_color(self) -> Optional[QuadrupleType[float]]: value = self._get_color() if value is not None: value = normalizers.normalizeColor(value) @@ -535,7 +535,7 @@ def _set_base_color(self, value: QuadrupleCollectionType[IntFloatType]) -> None: value = normalizers.normalizeColor(value) self._set_color(value) - def _get_color(self) -> QuadrupleType[float]: + def _get_color(self) -> Optional[QuadrupleType[float]]: """ "Get the native guideline's color. This is the environment implementation of the :attr:`BaseGuideline.color` diff --git a/Lib/fontParts/fontshell/anchor.py b/Lib/fontParts/fontshell/anchor.py index a06ce64c..b989080f 100644 --- a/Lib/fontParts/fontshell/anchor.py +++ b/Lib/fontParts/fontshell/anchor.py @@ -1,17 +1,32 @@ +from __future__ import annotations +from typing import Optional + import defcon from fontParts.base import BaseAnchor +from fontParts.base.annotations import ( + QuadrupleType, + QuadrupleCollectionType, + IntFloatType +) from fontParts.fontshell.base import RBaseObject class RAnchor(RBaseObject, BaseAnchor): wrapClass = defcon.Anchor - def _init(self, wrap=None): - if wrap is None: - wrap = self.wrapClass() - wrap.x = 0 - wrap.y = 0 - super(RAnchor, self)._init(wrap=wrap) + def _init(self, pathOrObject: Optional[defcon.Anchor] = None) -> None: + if self.wrapClass is not None: + if pathOrObject is None: + pathOrObject = self.wrapClass() + pathOrObject.x = 0 + pathOrObject.y = 0 + super(RAnchor, self)._init(pathOrObject=pathOrObject) + + def _getNaked(self) -> defcon.Anchor: + anchor = self.naked() + if anchor is None: + raise ValueError("Anchor cannot be None.") + return anchor # -------- # Position @@ -19,19 +34,19 @@ def _init(self, wrap=None): # x - def _get_x(self): - return self.naked().x + def _get_x(self) -> float: + return self._getNaked().x - def _set_x(self, value): - self.naked().x = value + def _set_x(self, value: float) -> None: + self._getNaked().x = value # y - def _get_y(self): - return self.naked().y + def _get_y(self) -> float: + return self._getNaked().y - def _set_y(self, value): - self.naked().y = value + def _set_y(self, value: float) -> None: + self._getNaked().y = value # -------------- # Identification @@ -39,32 +54,30 @@ def _set_y(self, value): # identifier - def _get_identifier(self): - anchor = self.naked() - return anchor.identifier + def _get_identifier(self) -> Optional[str]: + return self._getNaked().identifier - def _getIdentifier(self): - anchor = self.naked() - return anchor.generateIdentifier() + def _getIdentifier(self) -> str: + return self._getNaked().generateIdentifier() - def _setIdentifier(self, value): - self.naked().identifier = value + def _setIdentifier(self, value: str) -> None: + self._getNaked().identifier = value # name - def _get_name(self): - return self.naked().name + def _get_name(self) -> Optional[str]: + return self._getNaked().name - def _set_name(self, value): - self.naked().name = value + def _set_name(self, value: str) -> None: + self._getNaked().name = value # color - def _get_color(self): - value = self.naked().color - if value is not None: - value = tuple(value) - return value + def _get_color(self) -> Optional[QuadrupleType[float]]: + return self._getNaked().color - def _set_color(self, value): - self.naked().color = value + def _set_color( + self, + value: Optional[QuadrupleCollectionType[IntFloatType]] + ) -> None: + self._getNaked().color = value diff --git a/Lib/fontParts/fontshell/base.py b/Lib/fontParts/fontshell/base.py index 258613d8..276a1581 100644 --- a/Lib/fontParts/fontshell/base.py +++ b/Lib/fontParts/fontshell/base.py @@ -1,16 +1,25 @@ -class RBaseObject(object): - wrapClass = None +from __future__ import annotations +from typing import Generic, Optional, Type, TypeVar - def _init(self, wrap=None): - if wrap is None and self.wrapClass is not None: - wrap = self.wrapClass() - if wrap is not None: - self._wrapped = wrap +RBaseObjectType = TypeVar("RBaseObjectType", bound="RBaseObject") - def changed(self): - self.naked().dirty = True - def naked(self): +class RBaseObject(Generic[RBaseObjectType]): + wrapClass: Optional[Type[RBaseObjectType]] = None + dirty: bool + + def _init(self, pathOrObject: Optional[RBaseObjectType] = None) -> None: + if pathOrObject is not None: + self._wrapped = pathOrObject + if self.wrapClass is not None: + pathOrObject = self.wrapClass() + + def changed(self) -> None: + naked = self.naked() + if naked is not None: + naked.dirty = True + + def naked(self) -> Optional[RBaseObjectType]: if hasattr(self, "_wrapped"): return self._wrapped return None diff --git a/Lib/fontParts/fontshell/component.py b/Lib/fontParts/fontshell/component.py index 6ab5dd41..d15b8fac 100644 --- a/Lib/fontParts/fontshell/component.py +++ b/Lib/fontParts/fontshell/component.py @@ -1,10 +1,18 @@ +from __future__ import annotations +from typing import Optional, Type + import defcon from fontParts.base import BaseComponent +from fontParts.base.annotations import ( + SextupleType, + SextupleCollectionType, + IntFloatType, +) from fontParts.fontshell.base import RBaseObject class RComponent(RBaseObject, BaseComponent): - wrapClass = defcon.Component + wrapClass: Type[defcon.Component] = defcon.Component # ---------- # Attributes @@ -12,19 +20,27 @@ class RComponent(RBaseObject, BaseComponent): # baseGlyph - def _get_baseGlyph(self): - return self.naked().baseGlyph + def _get_baseGlyph(self) -> Optional[str]: + component = self.naked() + return component.baseGlyph if component else None - def _set_baseGlyph(self, value): - self.naked().baseGlyph = value + def _set_baseGlyph(self, value: str) -> None: + component = self.naked() + if component is not None: + component.baseGlyph = value # transformation - def _get_transformation(self): - return self.naked().transformation + def _get_transformation(self) -> SextupleType[float]: + component = self.naked() + if component is None: + raise ValueError("Naked cannot be None.") + return component.transformation - def _set_transformation(self, value): - self.naked().transformation = value + def _set_transformation(self, value: SextupleCollectionType[IntFloatType]) -> None: + component = self.naked() + if component is not None: + component.transformation = value # -------------- # Identification @@ -44,14 +60,20 @@ def _set_index(self, value): def _get_identifier(self): component = self.naked() + if component is None: + return None return component.identifier def _getIdentifier(self): component = self.naked() + if component is None: + return None return component.generateIdentifier() def _setIdentifier(self, value): - self.naked().identifier = value + component = self.naked() + if component is not None: + component.identifier = value # ------------- # Normalization diff --git a/Lib/fontParts/fontshell/contour.py b/Lib/fontParts/fontshell/contour.py index 3ee24dd9..46be715b 100644 --- a/Lib/fontParts/fontshell/contour.py +++ b/Lib/fontParts/fontshell/contour.py @@ -1,10 +1,17 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Any + import defcon from fontParts.base import BaseContour +from fontParts.base.annotations import PairCollectionType, QuadrupleType, IntFloatType from fontParts.fontshell.base import RBaseObject from fontParts.fontshell.point import RPoint from fontParts.fontshell.segment import RSegment from fontParts.fontshell.bPoint import RBPoint +if TYPE_CHECKING: + from fontParts.base import BasePoint + class RContour(RBaseObject, BaseContour): wrapClass = defcon.Contour @@ -12,107 +19,115 @@ class RContour(RBaseObject, BaseContour): segmentClass = RSegment bPointClass = RBPoint + def _getNaked(self) -> defcon.Contour: + contour = self.naked() + if contour is None: + raise ValueError("Contour cannot be None.") + return contour + # -------------- # Identification # -------------- # index - def _set_index(self, value): - contour = self.naked() + def _set_index(self, value: int) -> None: + contour = self._getNaked() glyph = contour.glyph - glyph.removeContour(contour) - glyph.insertContour(value, contour) + if glyph is not None: + glyph.removeContour(contour) + glyph.insertContour(value, contour) # identifier - def _get_identifier(self): - contour = self.naked() - return contour.identifier + def _get_identifier(self) -> Optional[str]: + return self._getNaked().identifier - def _getIdentifier(self): - contour = self.naked() - return contour.generateIdentifier() + def _getIdentifier(self) -> str: + return self._getNaked().generateIdentifier() - def _getIdentifierForPoint(self, point): - contour = self.naked() - point = point.naked() - return contour.generateIdentifierForPoint(point) + def _getIdentifierForPoint(self, point: BasePoint) -> str: + contour = self._getNaked() + nakedPoint = point.naked() + return contour.generateIdentifierForPoint(nakedPoint) # ---- # Open # ---- - def _get_open(self): - return self.naked().open + def _get_open(self) -> bool: + return self._getNaked().open # ------ # Bounds # ------ - def _get_bounds(self): - return self.naked().bounds + def _get_bounds(self) -> Optional[QuadrupleType[float]]: + return self._getNaked().bounds # ---- # Area # ---- - def _get_area(self): - return self.naked().area + def _get_area(self) -> Optional[float]: + return self._getNaked().area # --------- # Direction # --------- - def _get_clockwise(self): - return self.naked().clockwise + def _get_clockwise(self) -> bool: + return self._getNaked().clockwise - def _reverse(self, **kwargs): - self.naked().reverse() + def _reverse(self, **kwargs: Any) -> None: + self._getNaked().reverse() # ------------------------ # Point and Contour Inside # ------------------------ - def _pointInside(self, point): - return self.naked().pointInside(point) + def _pointInside(self, point: PairCollectionType[IntFloatType]) -> bool: + return self._getNaked().pointInside(point) - def _contourInside(self, otherContour): - return self.naked().contourInside(otherContour.naked(), segmentLength=5) + def _contourInside(self, otherContour: BaseContour) -> bool: + return self._getNaked().contourInside(otherContour.naked(), segmentLength=5) # ------ # Points # ------ - def _lenPoints(self, **kwargs): - return len(self.naked()) + def _lenPoints(self, **kwargs: Any) -> int: + return len(self._getNaked()) - def _getPoint(self, index, **kwargs): - contour = self.naked() + def _getPoint(self, index: int, **kwargs: Any) -> RPoint: + contour = self._getNaked() point = contour[index] return self.pointClass(point) def _insertPoint( self, - index, - position, - type=None, - smooth=None, - name=None, - identifier=None, - **kwargs, - ): + index: int, + position: PairCollectionType[IntFloatType], + type: Optional[str] = None, + smooth: Optional[bool] = None, + name: Optional[str] = None, + identifier: Optional[str] = None, + **kwargs: Any, + ) -> None: point = self.pointClass() point.x = position[0] point.y = position[1] point.type = type point.smooth = smooth point.name = name - point = point.naked() + nakedPoint = point.naked() + if nakedPoint is not None: + point = nakedPoint point.identifier = identifier - self.naked().insertPoint(index, point) + contour = self._getNaked() + contour.insertPoint(index, point) - def _removePoint(self, index, preserveCurve, **kwargs): - contour = self.naked() + def _removePoint(self, index: int, preserveCurve: bool, **kwargs: Any) -> None: + contour = self._getNaked() point = contour[index] contour.removePoint(point) diff --git a/Lib/fontParts/fontshell/features.py b/Lib/fontParts/fontshell/features.py index 30fb7d37..05f5cf5c 100644 --- a/Lib/fontParts/fontshell/features.py +++ b/Lib/fontParts/fontshell/features.py @@ -1,3 +1,6 @@ +from __future__ import annotations +from typing import Optional + import defcon from fontParts.base import BaseFeatures from fontParts.fontshell.base import RBaseObject @@ -6,8 +9,16 @@ class RFeatures(RBaseObject, BaseFeatures): wrapClass = defcon.Features - def _get_text(self): - return self.naked().text + def _getNaked(self) -> defcon.Features: + freatures = self.naked() + if freatures is None: + raise ValueError("Features cannot be None.") + return freatures + + def _get_text(self) -> Optional[str]: + features = self._getNaked() + return features.text - def _set_text(self, value): - self.naked().text = value + def _set_text(self, value: str) -> None: + features = self._getNaked() + features.text = value diff --git a/Lib/fontParts/fontshell/font.py b/Lib/fontParts/fontshell/font.py index cd4d22bb..ce59c7f9 100644 --- a/Lib/fontParts/fontshell/font.py +++ b/Lib/fontParts/fontshell/font.py @@ -1,5 +1,14 @@ -import defcon +from __future__ import annotations +from typing import Any, Optional, Tuple, Union import os + +import defcon +from fontParts.base.annotations import ( + CollectionType, + PairCollectionType, + QuadrupleCollectionType, + IntFloatType +) from fontParts.base import BaseFont from fontParts.fontshell.base import RBaseObject from fontParts.fontshell.info import RInfo @@ -21,45 +30,55 @@ class RFont(RBaseObject, BaseFont): layerClass = RLayer guidelineClass = RGuideline + def _getNaked(self) -> defcon.Font: + font = self.naked() + if font is None: + raise ValueError("Font cannot be None.") + return font + # --------------- # File Operations # --------------- # Initialize - def _init(self, pathOrObject=None, showInterface=True, **kwargs): - if pathOrObject is None: - font = self.wrapClass() - elif isinstance(pathOrObject, str): - font = self.wrapClass(pathOrObject) - elif hasattr(pathOrObject, "__fspath__"): - font = self.wrapClass(os.fspath(pathOrObject)) - else: - font = pathOrObject - self._wrapped = font + def _init( + self, + pathOrObject: Optional[Union[str, os.PathLike, defcon.Font]] = None, + showInterface: bool = True, + **kwargs: Any + ) -> None: + if self.wrapClass is not None: + if pathOrObject is None: + font = self.wrapClass() + elif isinstance(pathOrObject, (str, os.PathLike)): + font = self.wrapClass(os.fspath(pathOrObject)) + else: + font = pathOrObject + self._wrapped = font # path - def _get_path(self, **kwargs): - return self.naked().path + def _get_path(self, **kwargs: Any) -> Optional[str]: + return self._getNaked().path # save def _save( self, - path=None, - showProgress=False, - formatVersion=None, - fileStructure=None, - **kwargs, - ): - self.naked().save( + path: Optional[str] = None, + showProgress: bool = False, + formatVersion: Optional[int] = None, + fileStructure: Optional[str] = None, + **kwargs: Any, + ) -> None: + self._getNaked().save( path=path, formatVersion=formatVersion, structure=fileStructure ) # close - def _close(self, **kwargs): + def _close(self, **kwargs: Any) -> None: del self._wrapped # ----------- @@ -68,109 +87,126 @@ def _close(self, **kwargs): # info - def _get_info(self): - return self.infoClass(wrap=self.naked().info) + def _get_info(self) -> RInfo: + return self.infoClass(pathOrObject=self._getNaked().info) # groups - def _get_groups(self): - return self.groupsClass(wrap=self.naked().groups) + def _get_groups(self) -> RGroups: + return self.groupsClass(pathOrObject=self._getNaked().groups) # kerning - def _get_kerning(self): - return self.kerningClass(wrap=self.naked().kerning) + def _get_kerning(self) -> RKerning: + return self.kerningClass(pathOrObject=self._getNaked().kerning) # features - def _get_features(self): - return self.featuresClass(wrap=self.naked().features) + def _get_features(self) -> RFeatures: + return self.featuresClass(pathOrObject=self._getNaked().features) # lib - def _get_lib(self): - return self.libClass(wrap=self.naked().lib) + def _get_lib(self) -> RLib: + return self.libClass(pathOrObject=self._getNaked().lib) # tempLib - def _get_tempLib(self): - return self.libClass(wrap=self.naked().tempLib) + def _get_tempLib(self) -> RLib: + return self.libClass(pathOrObject=self._getNaked().tempLib) # ------ # Layers # ------ - def _get_layers(self, **kwargs): - return [self.layerClass(wrap=layer) for layer in self.naked().layers] + def _get_layers(self, **kwargs: Any) -> Tuple[RLayer, ...]: + return tuple( + self.layerClass(pathOrObject=layer) for layer in self._getNaked().layers + ) # order - def _get_layerOrder(self, **kwargs): - return self.naked().layers.layerOrder + def _get_layerOrder(self, **kwargs: Any) -> Tuple[str, ...]: + return self._getNaked().layers.layerOrder - def _set_layerOrder(self, value, **kwargs): - self.naked().layers.layerOrder = value + def _set_layerOrder(self, value: CollectionType[str], **kwargs: Any) -> None: + self._getNaked().layers.layerOrder = value # default layer - def _get_defaultLayerName(self): - return self.naked().layers.defaultLayer.name + def _get_defaultLayerName(self) -> str: + return self._getNaked().layers.defaultLayer.name - def _set_defaultLayerName(self, value, **kwargs): + def _set_defaultLayerName(self, value: str, **kwargs: Any) -> None: + font = self._getNaked() for layer in self.layers: if layer.name == value: break layer = layer.naked() - self.naked().layers.defaultLayer = layer + font.layers.defaultLayer = layer # new - def _newLayer(self, name, color, **kwargs): - layers = self.naked().layers + def _newLayer( + self, + name: str, + color: Optional[QuadrupleCollectionType[IntFloatType]], **kwargs: Any + ) -> RLayer: + layers = self._getNaked().layers layer = layers.newLayer(name) layer.color = color - return self.layerClass(wrap=layer) + return self.layerClass(pathOrObject=layer) # remove - def _removeLayer(self, name, **kwargs): - layers = self.naked().layers + def _removeLayer(self, name: str, **kwargs: Any) -> None: + layers = self._getNaked().layers del layers[name] # ------ # Glyphs # ------ - def _get_glyphOrder(self): - return self.naked().glyphOrder + def _get_glyphOrder(self) -> Tuple[str, ...]: + return self._getNaked().glyphOrder - def _set_glyphOrder(self, value): - self.naked().glyphOrder = value + def _set_glyphOrder(self, value: CollectionType[str]) -> None: + self._getNaked().glyphOrder = value # ---------- # Guidelines # ---------- - def _lenGuidelines(self, **kwargs): - return len(self.naked().guidelines) + def _lenGuidelines(self, **kwargs: Any) -> int: + return len(self._getNaked().guidelines) - def _getGuideline(self, index, **kwargs): - guideline = self.naked().guidelines[index] + def _getGuideline(self, index: int, **kwargs: Any) -> RGuideline: + guideline = self._getNaked().guidelines[index] return self.guidelineClass(guideline) def _appendGuideline( - self, position, angle, name=None, color=None, identifier=None, **kwargs - ): + self, + position: PairCollectionType[IntFloatType], + angle: Optional[float], + name: Optional[str] = None, + color: Optional[QuadrupleCollectionType[IntFloatType]] = None, + identifier: Optional[str] = None, + **kwargs: Any + ) -> RGuideline: + font = self._getNaked() guideline = self.guidelineClass().naked() + if guideline is None: + raise ValueError("Guideline cannot be None.") guideline.x = position[0] guideline.y = position[1] guideline.angle = angle guideline.name = name guideline.color = color guideline.identifier = identifier - self.naked().appendGuideline(guideline) + font.appendGuideline(guideline) return self.guidelineClass(guideline) - def _removeGuideline(self, index, **kwargs): - guideline = self.naked().guidelines[index] - self.naked().removeGuideline(guideline) + def _removeGuideline(self, index: int, **kwargs: Any) -> None: + font = self._getNaked() + guideline = font.guidelines[index] + font.removeGuideline(guideline) diff --git a/Lib/fontParts/fontshell/glyph.py b/Lib/fontParts/fontshell/glyph.py index 333177c8..1a7f4bca 100644 --- a/Lib/fontParts/fontshell/glyph.py +++ b/Lib/fontParts/fontshell/glyph.py @@ -1,6 +1,17 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Optional, Any, Union + import defcon import booleanOperations from fontParts.base import BaseGlyph +from fontParts.base.annotations import ( + CollectionType, + PairCollectionType, + QuadrupleType, + QuadrupleCollectionType, + SextupleCollectionType, + IntFloatType, +) from fontParts.base.errors import FontPartsError from fontParts.fontshell.base import RBaseObject from fontParts.fontshell.contour import RContour @@ -15,6 +26,14 @@ writeGlyphToString, ) +if TYPE_CHECKING: + from fontParts.base.image import BaseImage + from fontTools.pens.pointPen import SegmentToPointPen + from defcon.pens.glyphObjectPointPen import ( + GlyphObjectPointPen, + GlyphObjectLoadingPointPen + ) + class RGlyph(RBaseObject, BaseGlyph): wrapClass = defcon.Glyph @@ -25,25 +44,31 @@ class RGlyph(RBaseObject, BaseGlyph): imageClass = RImage libClass = RLib + def _getNaked(self) -> defcon.Glyph: + glyph = self.naked() + if glyph is None: + raise ValueError("Glyph cannot be None.") + return glyph + # -------------- # Identification # -------------- # Name - def _get_name(self): - return self.naked().name + def _get_name(self) -> str: + return self._getNaked().name - def _set_name(self, value): - self.naked().name = value + def _set_name(self, value: str) -> None: + self._getNaked().name = value # Unicodes - def _get_unicodes(self): - return self.naked().unicodes + def _get_unicodes(self) -> CollectionType[int]: + return self._getNaked().unicodes - def _set_unicodes(self, value): - self.naked().unicodes = value + def _set_unicodes(self, value: CollectionType[int]) -> None: + self._getNaked().unicodes = value # ------- # Metrics @@ -51,71 +76,67 @@ def _set_unicodes(self, value): # horizontal - def _get_width(self): - return self.naked().width + def _get_width(self) -> IntFloatType: + return self._getNaked().width - def _set_width(self, value): - self.naked().width = value + def _set_width(self, value: IntFloatType) -> None: + self._getNaked().width = value - def _get_leftMargin(self): - return self.naked().leftMargin + def _get_leftMargin(self) -> Optional[IntFloatType]: + return self._getNaked().leftMargin - def _set_leftMargin(self, value): - naked = self.naked() - naked.leftMargin = value + def _set_leftMargin(self, value: IntFloatType) -> None: + self._getNaked().leftMargin = value - def _get_rightMargin(self): - return self.naked().rightMargin + def _get_rightMargin(self) -> Optional[IntFloatType]: + return self._getNaked().rightMargin - def _set_rightMargin(self, value): - naked = self.naked() - naked.rightMargin = value + def _set_rightMargin(self, value: IntFloatType) -> None: + self._getNaked().rightMargin = value # vertical - def _get_height(self): - return self.naked().height + def _get_height(self) -> IntFloatType: + return self._getNaked().height - def _set_height(self, value): - self.naked().height = value + def _set_height(self, value: IntFloatType) -> None: + self._getNaked().height = value - def _get_bottomMargin(self): - return self.naked().bottomMargin + def _get_bottomMargin(self) -> Optional[IntFloatType]: + return self._getNaked().bottomMargin - def _set_bottomMargin(self, value): - naked = self.naked() - naked.bottomMargin = value + def _set_bottomMargin(self, value: IntFloatType) -> None: + self._getNaked().bottomMargin = value - def _get_topMargin(self): - return self.naked().topMargin + def _get_topMargin(self) -> Optional[IntFloatType]: + return self._getNaked().topMargin - def _set_topMargin(self, value): - naked = self.naked() - naked.topMargin = value + def _set_topMargin(self, value: IntFloatType) -> None: + self._getNaked().topMargin = value # ------ # Bounds # ------ - def _get_bounds(self): - return self.naked().bounds + def _get_bounds(self) -> Optional[QuadrupleType[IntFloatType]]: + return self._getNaked().bounds # ---- # Area # ---- - def _get_area(self): - return self.naked().area + def _get_area(self) -> Optional[float]: + return self._getNaked().area # ---- # Pens # ---- - def getPen(self): - return self.naked().getPen() + def getPen(self) -> SegmentToPointPen: + return self._getNaked().getPen() - def getPointPen(self): - return self.naked().getPointPen() + def getPointPen(self) -> Union[GlyphObjectPointPen, GlyphObjectLoadingPointPen]: + return self._getNaked().getPointPen() # ----------------------------------------- # Contour, Component and Anchor Interaction @@ -123,20 +144,19 @@ def getPointPen(self): # Contours - def _lenContours(self, **kwargs): - return len(self.naked()) + def _lenContours(self, **kwargs: Any) -> int: + return len(self._getNaked()) - def _getContour(self, index, **kwargs): - glyph = self.naked() - contour = glyph[index] + def _getContour(self, index: int, **kwargs: Any) -> RContour: + contour = self._getNaked()[index] return self.contourClass(contour) - def _removeContour(self, index, **kwargs): - glyph = self.naked() + def _removeContour(self, index: int, **kwargs: Any) -> None: + glyph = self._getNaked() contour = glyph[index] glyph.removeContour(contour) - def _removeOverlap(self, **kwargs): + def _removeOverlap(self, **kwargs: Any) -> None: if len(self): contours = list(self) for contour in contours: @@ -152,40 +172,48 @@ def _removeOverlap(self, **kwargs): ) booleanOperations.union(contours, self.getPointPen()) - def _correctDirection(self, trueType=False, **kwargs): - self.naked().correctContourDirection(trueType=trueType) + def _correctDirection(self, trueType: bool = False, **kwargs: Any) -> None: + self._getNaked().correctContourDirection(trueType=trueType) # Components - def _lenComponents(self, **kwargs): - return len(self.naked().components) + def _lenComponents(self, **kwargs: Any) -> int: + return len(self._getNaked().components) - def _getComponent(self, index, **kwargs): - glyph = self.naked() - component = glyph.components[index] + def _getComponent(self, index: int, **kwargs: Any) -> RComponent: + component = self._getNaked().components[index] return self.componentClass(component) - def _removeComponent(self, index, **kwargs): - glyph = self.naked() + def _removeComponent(self, index: int, **kwargs: Any) -> None: + glyph = self._getNaked() component = glyph.components[index] glyph.removeComponent(component) # Anchors - def _lenAnchors(self, **kwargs): - return len(self.naked().anchors) + def _lenAnchors(self, **kwargs: Any) -> int: + return len(self._getNaked().anchors) - def _getAnchor(self, index, **kwargs): - glyph = self.naked() - anchor = glyph.anchors[index] + def _getAnchor(self, index: int, **kwargs: Any) -> RAnchor: + anchor = self._getNaked().anchors[index] return self.anchorClass(anchor) - def _appendAnchor(self, name, position=None, color=None, identifier=None, **kwargs): - glyph = self.naked() + def _appendAnchor( + self, + name: str, + position: Optional[PairCollectionType[IntFloatType]] = None, + color: Optional[QuadrupleCollectionType[IntFloatType]] = None, + identifier: Optional[str] = None, + **kwargs: Any + ) -> RAnchor: + glyph = self._getNaked() anchor = self.anchorClass().naked() + if anchor is None: + raise ValueError("Anchor cannot be None") anchor.name = name - anchor.x = position[0] - anchor.y = position[1] + if position is not None: + anchor.x = position[0] + anchor.y = position[1] anchor.color = color anchor.identifier = identifier glyph.appendAnchor(anchor) @@ -193,26 +221,33 @@ def _appendAnchor(self, name, position=None, color=None, identifier=None, **kwar wrapped.glyph = self return wrapped - def _removeAnchor(self, index, **kwargs): - glyph = self.naked() + def _removeAnchor(self, index: int, **kwargs: Any) -> None: + glyph = self._getNaked() anchor = glyph.anchors[index] glyph.removeAnchor(anchor) # Guidelines - def _lenGuidelines(self, **kwargs): - return len(self.naked().guidelines) + def _lenGuidelines(self, **kwargs: Any) -> int: + return len(self._getNaked().guidelines) - def _getGuideline(self, index, **kwargs): - glyph = self.naked() - guideline = glyph.guidelines[index] + def _getGuideline(self, index: int, **kwargs: Any) -> RGuideline: + guideline = self._getNaked().guidelines[index] return self.guidelineClass(guideline) def _appendGuideline( - self, position, angle, name=None, color=None, identifier=None, **kwargs - ): - glyph = self.naked() + self, + position: Optional[PairCollectionType[IntFloatType]], + angle: float, + name: Optional[str] = None, + color: Optional[QuadrupleCollectionType[IntFloatType]] = None, + identifier: Optional[str] = None, + **kwargs: Any + ) -> RGuideline: + glyph = self._getNaked() guideline = self.guidelineClass().naked() + if guideline is None: + raise ValueError("Guideline cannot be None") guideline.x = position[0] guideline.y = position[1] guideline.angle = angle @@ -222,8 +257,8 @@ def _appendGuideline( glyph.appendGuideline(guideline) return self.guidelineClass(guideline) - def _removeGuideline(self, index, **kwargs): - glyph = self.naked() + def _removeGuideline(self, index: int, **kwargs: Any) -> None: + glyph = self._getNaked() guideline = glyph.guidelines[index] glyph.removeGuideline(guideline) @@ -233,7 +268,7 @@ def _removeGuideline(self, index, **kwargs): # new - def _newLayer(self, name, **kwargs): + def _newLayer(self, name: str, **kwargs: Any) -> RGlyph: layerName = name glyphName = self.name font = self.font @@ -246,7 +281,7 @@ def _newLayer(self, name, **kwargs): # remove - def _removeLayer(self, name, **kwargs): + def _removeLayer(self, name: str, **kwargs: Any) -> None: layerName = name glyphName = self.name font = self.font @@ -257,22 +292,27 @@ def _removeLayer(self, name, **kwargs): # Image # ----- - def _get_image(self): - image = self.naked().image + def _get_image(self) -> Optional[RImage]: + image = self._getNaked().image if image is None: return None return self.imageClass(image) - def _addImage(self, data, transformation=None, color=None): - image = self.naked().image + def _addImage( + self, + data: bytes, + transformation: Optional[SextupleCollectionType[IntFloatType]] = None, + color: Optional[QuadrupleCollectionType[IntFloatType]] = None, + ) -> None: + image = self._getNaked().image image = self.imageClass(image) image.glyph = self image.data = data image.transformation = transformation image.color = color - def _clearImage(self, **kwargs): - self.naked().image = None + def _clearImage(self, **kwargs: Any) -> None: + self._getNaked().image = None # ---- # Note @@ -280,22 +320,22 @@ def _clearImage(self, **kwargs): # Mark - def _get_markColor(self): - value = self.naked().markColor + def _get_markColor(self) -> Optional[QuadrupleType[float]]: + value = self._getNaked().markColor if value is not None: value = tuple(value) return value - def _set_markColor(self, value): - self.naked().markColor = value + def _set_markColor(self, value: Optional[QuadrupleCollectionType[IntFloatType]]) -> None: + self._getNaked().markColor = value # Note - def _get_note(self): - return self.naked().note + def _get_note(self) -> Optional[str]: + return self._getNaked().note - def _set_note(self, value): - self.naked().note = value + def _set_note(self, value: Optional[str]) -> None: + self._getNaked().note = value # ----------- # Sub-Objects @@ -303,31 +343,31 @@ def _set_note(self, value): # lib - def _get_lib(self): - return self.libClass(wrap=self.naked().lib) + def _get_lib(self) -> RLib: + return self.libClass(wrap=self._getNaked().lib) # tempLib - def _get_tempLib(self): - return self.libClass(wrap=self.naked().tempLib) + def _get_tempLib(self) -> RLib: + return self.libClass(wrap=self._getNaked().tempLib) # --- # API # --- - def _loadFromGLIF(self, glifData, validate=True): + def _loadFromGLIF(self, glifData: str, validate: bool = True) -> None: try: readGlyphFromString( aString=glifData, - glyphObject=self.naked(), + glyphObject=self._getNaked(), pointPen=self.getPointPen(), validate=validate, ) - except GlifLibError: - raise FontPartsError("Not valid glif data") + except GlifLibError as e: + raise FontPartsError("Not valid glif data") from e - def _dumpToGLIF(self, glyphFormatVersion): - glyph = self.naked() + def _dumpToGLIF(self, glyphFormatVersion: int) -> str: + glyph = self._getNaked() return writeGlyphToString( glyphName=glyph.name, glyphObject=glyph, diff --git a/Lib/fontParts/fontshell/groups.py b/Lib/fontParts/fontshell/groups.py index 66c86b2c..9271cd62 100644 --- a/Lib/fontParts/fontshell/groups.py +++ b/Lib/fontParts/fontshell/groups.py @@ -1,28 +1,51 @@ +from __future__ import annotations +from typing import Tuple, Dict, ItemsView + import defcon from fontParts.base import BaseGroups +from fontParts.base.annotations import CollectionType from fontParts.fontshell.base import RBaseObject +ValueType = Tuple[str, ...] +GroupsDict = Dict[str, ValueType] + class RGroups(RBaseObject, BaseGroups): wrapClass = defcon.Groups - def _get_side1KerningGroups(self): - return self.naked().getRepresentation("defcon.groups.kerningSide1Groups") - - def _get_side2KerningGroups(self): - return self.naked().getRepresentation("defcon.groups.kerningSide2Groups") - - def _items(self): - return self.naked().items() - - def _contains(self, key): - return key in self.naked() - - def _setItem(self, key, value): - self.naked()[key] = list(value) - - def _getItem(self, key): - return self.naked()[key] - - def _delItem(self, key): - del self.naked()[key] + def _getNaked(self) -> defcon.Groups: + groups = self.naked() + if groups is None: + raise ValueError("Groups cannot be None.") + return groups + + def _get_side1KerningGroups(self) -> GroupsDict: + groups = self._getNaked() + representation = groups.getRepresentation("defcon.groups.kerningSide1Groups") + return {k: tuple(v) for k, v in representation.items()} + + def _get_side2KerningGroups(self) -> GroupsDict: + groups = self._getNaked() + representation = groups.getRepresentation("defcon.groups.kerningSide2Groups") + return {k: tuple(v) for k, v in representation.items()} + + def _items(self) -> ItemsView[str, Tuple[str]]: + groups = self._getNaked() + formatted = {k: tuple(v) for k, v in groups.items()} + return formatted.items() + + def _contains(self, key: str) -> bool: + groups = self._getNaked() + return key in groups + + def _setItem(self, key: str, value: CollectionType[str]) -> None: + groups = self._getNaked() + groups[key] = tuple(value) + + def _getItem(self, key: str) -> Tuple[str]: + groups = self._getNaked() + return tuple(groups[key]) + + def _delItem(self, key: str) -> None: + groups = self._getNaked() + del groups[key] diff --git a/Lib/fontParts/fontshell/guideline.py b/Lib/fontParts/fontshell/guideline.py index 57ba3aea..7effc46d 100644 --- a/Lib/fontParts/fontshell/guideline.py +++ b/Lib/fontParts/fontshell/guideline.py @@ -1,18 +1,34 @@ +from __future__ import annotations +from typing import Optional + import defcon from fontParts.base import BaseGuideline +from fontParts.base.annotations import ( + QuadrupleType, + QuadrupleCollectionType, + IntFloatType +) from fontParts.fontshell.base import RBaseObject class RGuideline(RBaseObject, BaseGuideline): wrapClass = defcon.Guideline - def _init(self, wrap=None): - if wrap is None: - wrap = self.wrapClass() - wrap.x = 0 - wrap.y = 0 - wrap.angle = 0 - super(RGuideline, self)._init(wrap=wrap) + def _init(self, pathOrObject: Optional[defcon.Guideline] = None) -> None: + if self.wrapClass is not None: + if pathOrObject is None: + pathOrObject = self.wrapClass() + if pathOrObject is not None: + pathOrObject.x = 0 + pathOrObject.y = 0 + pathOrObject.angle = 0 + super(RGuideline, self)._init(pathOrObject=pathOrObject) + + def _getNaked(self) -> defcon.Guideline: + guideline = self.naked() + if guideline is None: + raise ValueError("Guideline cannot be None.") + return guideline # -------- # Position @@ -20,27 +36,27 @@ def _init(self, wrap=None): # x - def _get_x(self): - return self.naked().x + def _get_x(self) -> float: + return self._getNaked().x - def _set_x(self, value): - self.naked().x = value + def _set_x(self, value: float) -> None: + self._getNaked().x = value # y - def _get_y(self): - return self.naked().y + def _get_y(self) -> float: + return self._getNaked().y - def _set_y(self, value): - self.naked().y = value + def _set_y(self, value: float) -> None: + self._getNaked().y = value # angle - def _get_angle(self): - return self.naked().angle + def _get_angle(self) -> float: + return self._getNaked().angle - def _set_angle(self, value): - self.naked().angle = value + def _set_angle(self, value: Optional[IntFloatType]) -> None: + self._getNaked().angle = value # -------------- # Identification @@ -48,32 +64,30 @@ def _set_angle(self, value): # identifier - def _get_identifier(self): - guideline = self.naked() - return guideline.identifier + def _get_identifier(self) -> Optional[str]: + return self._getNaked().identifier - def _getIdentifier(self): - guideline = self.naked() - return guideline.generateIdentifier() + def _getIdentifier(self) -> str: + return self._getNaked().generateIdentifier() - def _setIdentifier(self, value): - self.naked().identifier = value + def _setIdentifier(self, value: str) -> None: + self._getNaked().identifier = value # name - def _get_name(self): - return self.naked().name + def _get_name(self) -> Optional[str]: + return self._getNaked().name - def _set_name(self, value): - self.naked().name = value + def _set_name(self, value: Optional[str]) -> None: + self._getNaked().name = value # color - def _get_color(self): - value = self.naked().color + def _get_color(self) -> Optional[QuadrupleType[float]]: + value = self._getNaked().color if value is not None: value = tuple(value) return value - def _set_color(self, value): - self.naked().color = value + def _set_color(self, value: Optional[QuadrupleCollectionType[float]]) -> None: + self._getNaked().color = value diff --git a/Lib/fontParts/fontshell/image.py b/Lib/fontParts/fontshell/image.py index 4cc664e8..439255e4 100644 --- a/Lib/fontParts/fontshell/image.py +++ b/Lib/fontParts/fontshell/image.py @@ -1,12 +1,28 @@ +from __future__ import annotations +from typing import Optional + import defcon +from fontTools.ufoLib.validators import pngValidator from fontParts.base import BaseImage, FontPartsError +from fontParts.base.annotations import ( + QuadrupleType, + SextupleType, + QuadrupleCollectionType, + SextupleCollectionType +) from fontParts.fontshell.base import RBaseObject class RImage(RBaseObject, BaseImage): wrapClass = defcon.Image - _orphanData = None - _orphanColor = None + _orphanData: Optional[bytes] = None + _orphanColor: Optional[QuadrupleCollectionType[float]] = None + + def _getNaked(self) -> defcon.Image: + image = self.naked() + if image is None: + raise ValueError("Image cannot be None.") + return image # ---------- # Attributes @@ -14,34 +30,35 @@ class RImage(RBaseObject, BaseImage): # Transformation - def _get_transformation(self): - return self.naked().transformation + def _get_transformation(self) -> SextupleType[float]: + return self._getNaked().transformation - def _set_transformation(self, value): - self.naked().transformation = value + def _set_transformation(self, value: SextupleCollectionType[float]) -> None: + self._getNaked().transformation = value # Color - def _get_color(self): - if self.font is None: - return self._orphanColor - value = self.naked().color + def _get_color(self) -> Optional[QuadrupleType[float]]: + if self.font is None and self._orphanColor is not None: + r, g, b, a = self._orphanColor + return (r, g, b, a) + value = self._getNaked().color if value is not None: value = tuple(value) return value - def _set_color(self, value): + def _set_color(self, value: Optional[QuadrupleCollectionType[float]]) -> None: if self.font is None: self._orphanColor = value else: - self.naked().color = value + self._getNaked().color = value # Data - def _get_data(self): + def _get_data(self) -> Optional[bytes]: if self.font is None: return self._orphanData - image = self.naked() + image = self._getNaked() images = self.font.naked().images fileName = image.fileName if fileName is None: @@ -50,8 +67,7 @@ def _get_data(self): return None return images[fileName] - def _set_data(self, value): - from fontTools.ufoLib.validators import pngValidator + def _set_data(self, value: bytes) -> None: if not isinstance(value, bytes): raise FontPartsError("The image data provided is not valid.") @@ -60,7 +76,7 @@ def _set_data(self, value): if self.font is None: self._orphanData = value else: - image = self.naked() + image = self._getNaked() images = image.font.images fileName = images.findDuplicateImage(value) if fileName is None: From da1ab1c349406eca4262f9a8082b9049a77d2fa5 Mon Sep 17 00:00:00 2001 From: knutnergaard <15194233+knutnergaard@users.noreply.github.com> Date: Sun, 16 Mar 2025 12:30:46 +0000 Subject: [PATCH 2/8] Format fixes by ruff --- Lib/fontParts/base/font.py | 9 +++------ Lib/fontParts/base/glyph.py | 4 ++-- Lib/fontParts/fontshell/anchor.py | 5 ++--- Lib/fontParts/fontshell/font.py | 19 ++++++++++--------- Lib/fontParts/fontshell/glyph.py | 10 ++++++---- Lib/fontParts/fontshell/guideline.py | 2 +- Lib/fontParts/fontshell/image.py | 3 +-- 7 files changed, 25 insertions(+), 27 deletions(-) diff --git a/Lib/fontParts/base/font.py b/Lib/fontParts/base/font.py index cc5d7311..97aaad54 100644 --- a/Lib/fontParts/base/font.py +++ b/Lib/fontParts/base/font.py @@ -275,7 +275,7 @@ def save( """ if path is None and self.path is None: raise IOError( - ("The font cannot be saved because no file " "location has been given.") + ("The font cannot be saved because no file location has been given.") ) if path is not None: path = normalizers.normalizeFilePath(path) @@ -468,10 +468,7 @@ def generate( ext = self.generateFormatToExtension(format, "." + format) if path is None and self.path is None: raise IOError( - ( - "The file cannot be generated because an " - "output path was not defined." - ) + ("The file cannot be generated because an output path was not defined.") ) elif path is None: path = os.path.splitext(self.path)[0] @@ -1854,7 +1851,7 @@ def _appendGuideline( # type: ignore[return] angle: Optional[float], name: Optional[str], color: Optional[QuadrupleCollectionType[IntFloatType]], - **kwargs: Any + **kwargs: Any, ) -> BaseGuideline: r"""Append a new guideline to the native font. diff --git a/Lib/fontParts/base/glyph.py b/Lib/fontParts/base/glyph.py index 44696d45..32a6b2ad 100644 --- a/Lib/fontParts/base/glyph.py +++ b/Lib/fontParts/base/glyph.py @@ -2401,7 +2401,7 @@ def scaleBy( normalizedOrigin = normalizers.normalizeCoordinateTuple(origin) if normalizedOrigin != (0, 0) and (width or height): raise FontPartsError( - ("The origin must not be set when " "scaling the width or height.") + ("The origin must not be set when scaling the width or height.") ) super(BaseGlyph, self).scaleBy(normalizedValue, origin=normalizedOrigin) sX, sY = normalizedValue @@ -3248,7 +3248,7 @@ def _get_base_image(self) -> BaseImage: def _get_image(self) -> Optional[BaseImage]: # type: ignore[return] """Get the image for the native glyph. - :return: The :class:`BaseImage` subclass instance belonging to the glyph, + :return: The :class:`BaseImage` subclass instance belonging to the glyph, or :obj:`None` if the glyph has no image. :raises NotImplementedError: If the method has not been overridden by a subclass. diff --git a/Lib/fontParts/fontshell/anchor.py b/Lib/fontParts/fontshell/anchor.py index b989080f..b415763b 100644 --- a/Lib/fontParts/fontshell/anchor.py +++ b/Lib/fontParts/fontshell/anchor.py @@ -6,7 +6,7 @@ from fontParts.base.annotations import ( QuadrupleType, QuadrupleCollectionType, - IntFloatType + IntFloatType, ) from fontParts.fontshell.base import RBaseObject @@ -77,7 +77,6 @@ def _get_color(self) -> Optional[QuadrupleType[float]]: return self._getNaked().color def _set_color( - self, - value: Optional[QuadrupleCollectionType[IntFloatType]] + self, value: Optional[QuadrupleCollectionType[IntFloatType]] ) -> None: self._getNaked().color = value diff --git a/Lib/fontParts/fontshell/font.py b/Lib/fontParts/fontshell/font.py index ce59c7f9..2cc743c1 100644 --- a/Lib/fontParts/fontshell/font.py +++ b/Lib/fontParts/fontshell/font.py @@ -7,7 +7,7 @@ CollectionType, PairCollectionType, QuadrupleCollectionType, - IntFloatType + IntFloatType, ) from fontParts.base import BaseFont from fontParts.fontshell.base import RBaseObject @@ -43,10 +43,10 @@ def _getNaked(self) -> defcon.Font: # Initialize def _init( - self, - pathOrObject: Optional[Union[str, os.PathLike, defcon.Font]] = None, - showInterface: bool = True, - **kwargs: Any + self, + pathOrObject: Optional[Union[str, os.PathLike, defcon.Font]] = None, + showInterface: bool = True, + **kwargs: Any, ) -> None: if self.wrapClass is not None: if pathOrObject is None: @@ -148,9 +148,10 @@ def _set_defaultLayerName(self, value: str, **kwargs: Any) -> None: # new def _newLayer( - self, - name: str, - color: Optional[QuadrupleCollectionType[IntFloatType]], **kwargs: Any + self, + name: str, + color: Optional[QuadrupleCollectionType[IntFloatType]], + **kwargs: Any, ) -> RLayer: layers = self._getNaked().layers layer = layers.newLayer(name) @@ -191,7 +192,7 @@ def _appendGuideline( name: Optional[str] = None, color: Optional[QuadrupleCollectionType[IntFloatType]] = None, identifier: Optional[str] = None, - **kwargs: Any + **kwargs: Any, ) -> RGuideline: font = self._getNaked() guideline = self.guidelineClass().naked() diff --git a/Lib/fontParts/fontshell/glyph.py b/Lib/fontParts/fontshell/glyph.py index 1a7f4bca..7c897114 100644 --- a/Lib/fontParts/fontshell/glyph.py +++ b/Lib/fontParts/fontshell/glyph.py @@ -31,7 +31,7 @@ from fontTools.pens.pointPen import SegmentToPointPen from defcon.pens.glyphObjectPointPen import ( GlyphObjectPointPen, - GlyphObjectLoadingPointPen + GlyphObjectLoadingPointPen, ) @@ -204,7 +204,7 @@ def _appendAnchor( position: Optional[PairCollectionType[IntFloatType]] = None, color: Optional[QuadrupleCollectionType[IntFloatType]] = None, identifier: Optional[str] = None, - **kwargs: Any + **kwargs: Any, ) -> RAnchor: glyph = self._getNaked() anchor = self.anchorClass().naked() @@ -242,7 +242,7 @@ def _appendGuideline( name: Optional[str] = None, color: Optional[QuadrupleCollectionType[IntFloatType]] = None, identifier: Optional[str] = None, - **kwargs: Any + **kwargs: Any, ) -> RGuideline: glyph = self._getNaked() guideline = self.guidelineClass().naked() @@ -326,7 +326,9 @@ def _get_markColor(self) -> Optional[QuadrupleType[float]]: value = tuple(value) return value - def _set_markColor(self, value: Optional[QuadrupleCollectionType[IntFloatType]]) -> None: + def _set_markColor( + self, value: Optional[QuadrupleCollectionType[IntFloatType]] + ) -> None: self._getNaked().markColor = value # Note diff --git a/Lib/fontParts/fontshell/guideline.py b/Lib/fontParts/fontshell/guideline.py index 7effc46d..8545d897 100644 --- a/Lib/fontParts/fontshell/guideline.py +++ b/Lib/fontParts/fontshell/guideline.py @@ -6,7 +6,7 @@ from fontParts.base.annotations import ( QuadrupleType, QuadrupleCollectionType, - IntFloatType + IntFloatType, ) from fontParts.fontshell.base import RBaseObject diff --git a/Lib/fontParts/fontshell/image.py b/Lib/fontParts/fontshell/image.py index 439255e4..afd38ef9 100644 --- a/Lib/fontParts/fontshell/image.py +++ b/Lib/fontParts/fontshell/image.py @@ -8,7 +8,7 @@ QuadrupleType, SextupleType, QuadrupleCollectionType, - SextupleCollectionType + SextupleCollectionType, ) from fontParts.fontshell.base import RBaseObject @@ -68,7 +68,6 @@ def _get_data(self) -> Optional[bytes]: return images[fileName] def _set_data(self, value: bytes) -> None: - if not isinstance(value, bytes): raise FontPartsError("The image data provided is not valid.") if not pngValidator(data=value)[0]: From 929d0223fe4d728945785b8df66197ca0092fc1e Mon Sep 17 00:00:00 2001 From: knutnergaard Date: Sun, 16 Mar 2025 16:03:29 +0100 Subject: [PATCH 3/8] Annotate more modules and correct base classes. --- Lib/fontParts/base/base.py | 1 - Lib/fontParts/base/groups.py | 6 +-- Lib/fontParts/base/layer.py | 6 +-- Lib/fontParts/base/lib.py | 8 +-- Lib/fontParts/fontshell/glyph.py | 4 +- Lib/fontParts/fontshell/guideline.py | 5 +- Lib/fontParts/fontshell/image.py | 10 ++-- Lib/fontParts/fontshell/layer.py | 65 +++++++++++++++--------- Lib/fontParts/fontshell/lib.py | 32 ++++++++---- Lib/fontParts/fontshell/point.py | 74 +++++++++++++++------------- 10 files changed, 128 insertions(+), 83 deletions(-) diff --git a/Lib/fontParts/base/base.py b/Lib/fontParts/base/base.py index e355e166..883298b1 100644 --- a/Lib/fontParts/base/base.py +++ b/Lib/fontParts/base/base.py @@ -25,7 +25,6 @@ from fontParts.base import normalizers from fontParts.base.annotations import ( PairType, - CollectionType, PairCollectionType, SextupleCollectionType, IntFloatType, diff --git a/Lib/fontParts/base/groups.py b/Lib/fontParts/base/groups.py index 1ad554ff..5c4eb014 100644 --- a/Lib/fontParts/base/groups.py +++ b/Lib/fontParts/base/groups.py @@ -18,9 +18,9 @@ if TYPE_CHECKING: from fontParts.base.font import BaseFont - from fontparts.base import BaseItems - from fontparts.base import BaseKeys - from fontparts.base import BaseValues + from fontParts.base.base import BaseKeys + from fontParts.base.base import BaseItems + from fontParts.base.base import BaseValues ValueType = Tuple[str, ...] GroupsDict = Dict[str, ValueType] diff --git a/Lib/fontParts/base/layer.py b/Lib/fontParts/base/layer.py index 480bc146..10cd3348 100644 --- a/Lib/fontParts/base/layer.py +++ b/Lib/fontParts/base/layer.py @@ -815,19 +815,19 @@ def _set_name(self, value: str, **kwargs: Any) -> None: """, ) - def _get_base_color(self) -> QuadrupleCollectionType[IntFloatType]: + def _get_base_color(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: value = self._get_color() if value is not None: value = normalizers.normalizeColor(value) value = Color(value) return value - def _set_base_color(self, value: QuadrupleCollectionType[IntFloatType]) -> None: + def _set_base_color(self, value: Optional[QuadrupleCollectionType[IntFloatType]]) -> None: if value is not None: value = normalizers.normalizeColor(value) self._set_color(value) - def _get_color(self) -> QuadrupleCollectionType[IntFloatType]: # type: ignore[return] + def _get_color(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: # type: ignore[return] """Get the color of the layer. This is the environment implementation of diff --git a/Lib/fontParts/base/lib.py b/Lib/fontParts/base/lib.py index 7f27652d..1f8e6840 100644 --- a/Lib/fontParts/base/lib.py +++ b/Lib/fontParts/base/lib.py @@ -18,10 +18,10 @@ if TYPE_CHECKING: from fontParts.base.glyph import BaseGlyph from fontParts.base.font import BaseFont - from fontparts.base.layer import BaseLayer - from fontparts.base import BaseItems - from fontparts.base import BaseKeys - from fontparts.base import BaseValues + from fontParts.base.layer import BaseLayer + from fontParts.base.base import BaseItems + from fontParts.base.base import BaseValues + from fontParts.base.base import BaseKeys class BaseLib(BaseDict, DeprecatedLib, RemovedLib): diff --git a/Lib/fontParts/fontshell/glyph.py b/Lib/fontParts/fontshell/glyph.py index 1a7f4bca..bee2c752 100644 --- a/Lib/fontParts/fontshell/glyph.py +++ b/Lib/fontParts/fontshell/glyph.py @@ -326,7 +326,9 @@ def _get_markColor(self) -> Optional[QuadrupleType[float]]: value = tuple(value) return value - def _set_markColor(self, value: Optional[QuadrupleCollectionType[IntFloatType]]) -> None: + def _set_markColor( + self, value: Optional[QuadrupleCollectionType[IntFloatType]] + ) -> None: self._getNaked().markColor = value # Note diff --git a/Lib/fontParts/fontshell/guideline.py b/Lib/fontParts/fontshell/guideline.py index 7effc46d..88b9cbf4 100644 --- a/Lib/fontParts/fontshell/guideline.py +++ b/Lib/fontParts/fontshell/guideline.py @@ -89,5 +89,8 @@ def _get_color(self) -> Optional[QuadrupleType[float]]: value = tuple(value) return value - def _set_color(self, value: Optional[QuadrupleCollectionType[float]]) -> None: + def _set_color( + self, + value: Optional[QuadrupleCollectionType[IntFloatType]] + ) -> None: self._getNaked().color = value diff --git a/Lib/fontParts/fontshell/image.py b/Lib/fontParts/fontshell/image.py index 439255e4..8680b90f 100644 --- a/Lib/fontParts/fontshell/image.py +++ b/Lib/fontParts/fontshell/image.py @@ -8,7 +8,8 @@ QuadrupleType, SextupleType, QuadrupleCollectionType, - SextupleCollectionType + SextupleCollectionType, + IntFloatType ) from fontParts.fontshell.base import RBaseObject @@ -16,7 +17,7 @@ class RImage(RBaseObject, BaseImage): wrapClass = defcon.Image _orphanData: Optional[bytes] = None - _orphanColor: Optional[QuadrupleCollectionType[float]] = None + _orphanColor: Optional[QuadrupleCollectionType[IntFloatType]] = None def _getNaked(self) -> defcon.Image: image = self.naked() @@ -47,7 +48,10 @@ def _get_color(self) -> Optional[QuadrupleType[float]]: value = tuple(value) return value - def _set_color(self, value: Optional[QuadrupleCollectionType[float]]) -> None: + def _set_color( + self, + value: Optional[QuadrupleCollectionType[IntFloatType]] + ) -> None: if self.font is None: self._orphanColor = value else: diff --git a/Lib/fontParts/fontshell/layer.py b/Lib/fontParts/fontshell/layer.py index 84162134..dd4fd98f 100644 --- a/Lib/fontParts/fontshell/layer.py +++ b/Lib/fontParts/fontshell/layer.py @@ -1,5 +1,12 @@ +from __future__ import annotations +from typing import Optional, Tuple, Dict, Any + import defcon from fontParts.base import BaseLayer +from fontParts.base.annotations import ( + QuadrupleCollectionType, + IntFloatType, +) from fontParts.fontshell.base import RBaseObject from fontParts.fontshell.lib import RLib from fontParts.fontshell.glyph import RGlyph @@ -10,19 +17,25 @@ class RLayer(RBaseObject, BaseLayer): libClass = RLib glyphClass = RGlyph + def _getNaked(self) -> defcon.Layer: + layer = self.naked() + if layer is None: + raise ValueError("Layer cannot be None.") + return layer + # ----------- # Sub-Objects # ----------- # lib - def _get_lib(self): - return self.libClass(wrap=self.naked().lib) + def _get_lib(self) -> RLib: + return self.libClass(wrap=self._getNaked().lib) # tempLib - def _get_tempLib(self): - return self.libClass(wrap=self.naked().tempLib) + def _get_tempLib(self) -> RLib: + return self.libClass(wrap=self._getNaked().tempLib) # -------------- # Identification @@ -30,52 +43,56 @@ def _get_tempLib(self): # name - def _get_name(self): - return self.naked().name + def _get_name(self) -> str: + return self._getNaked().name - def _set_name(self, value, **kwargs): - self.naked().name = value + def _set_name(self, value: str, **kwargs: Any) -> None: + self._getNaked().name = value # color - def _get_color(self): - value = self.naked().color + def _get_color(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: + value = self._getNaked().color if value is not None: value = tuple(value) return value - def _set_color(self, value, **kwargs): - self.naked().color = value + def _set_color( + self, + value: Optional[QuadrupleCollectionType[IntFloatType]], + **kwargs: Any + ) -> None: + self._getNaked().color = value # ----------------- # Glyph Interaction # ----------------- - def _getItem(self, name, **kwargs): - layer = self.naked() + def _getItem(self, name: str, **kwargs: Any) -> RGlyph: + layer = self._getNaked() glyph = layer[name] return self.glyphClass(glyph) - def _keys(self, **kwargs): - return self.naked().keys() + def _keys(self, **kwargs: Any) -> Tuple[str, ...]: + return tuple(self._getNaked().keys()) - def _newGlyph(self, name, **kwargs): - layer = self.naked() + def _newGlyph(self, name: str, **kwargs: Any) -> RGlyph: + layer = self._getNaked() layer.newGlyph(name) return self[name] - def _removeGlyph(self, name, **kwargs): - layer = self.naked() + def _removeGlyph(self, name: str, **kwargs: Any) -> None: + layer = self._getNaked() del layer[name] # ------- # mapping # ------- - def _getReverseComponentMapping(self): - mapping = self.naked().componentReferences + def _getReverseComponentMapping(self) -> Dict[str, Tuple[str, ...]]: + mapping = self._getNaked().componentReferences return {k: tuple(v) for k, v in mapping.items()} - def _getCharacterMapping(self): - mapping = self.naked().unicodeData + def _getCharacterMapping(self) -> Dict[int, Tuple[str, ...]]: + mapping = self._getNaked().unicodeData return {k: tuple(v) for k, v in mapping.items()} diff --git a/Lib/fontParts/fontshell/lib.py b/Lib/fontParts/fontshell/lib.py index dadf870a..1e38eab2 100644 --- a/Lib/fontParts/fontshell/lib.py +++ b/Lib/fontParts/fontshell/lib.py @@ -1,22 +1,34 @@ +from __future__ import annotations +from typing import TYPE_CHECKING, Any + import defcon from fontParts.base import BaseLib from fontParts.fontshell.base import RBaseObject +if TYPE_CHECKING: + from collections.abc import ItemsView + class RLib(RBaseObject, BaseLib): wrapClass = defcon.Lib - def _items(self): - return self.naked().items() + def _getNaked(self) -> defcon.Lib: + lib = self.naked() + if lib is None: + raise ValueError("Lib cannot be None.") + return lib + + def _items(self) -> ItemsView[str, Any]: + return self._getNaked().items() - def _contains(self, key): - return key in self.naked() + def _contains(self, key: str) -> bool: + return key in self._getNaked() - def _setItem(self, key, value): - self.naked()[key] = value + def _setItem(self, key: str, value: Any) -> None: + self._getNaked()[key] = value - def _getItem(self, key): - return self.naked()[key] + def _getItem(self, key: str) -> Any: + return self._getNaked()[key] - def _delItem(self, key): - del self.naked()[key] + def _delItem(self, key: str) -> None: + del self._getNaked()[key] diff --git a/Lib/fontParts/fontshell/point.py b/Lib/fontParts/fontshell/point.py index 14cbba5f..f1ccc6cf 100644 --- a/Lib/fontParts/fontshell/point.py +++ b/Lib/fontParts/fontshell/point.py @@ -1,4 +1,9 @@ +from __future__ import annotations +from typing import Optional + import defcon + +from fontParts.base.annotations import IntFloatType from fontParts.base import BasePoint, FontPartsError from fontParts.fontshell.base import RBaseObject @@ -6,64 +11,68 @@ class RPoint(RBaseObject, BasePoint): wrapClass = defcon.Point - def _init(self, wrap=None): - if wrap is None: - wrap = self.wrapClass((0, 0)) - super(RPoint, self)._init(wrap=wrap) + def _init(self, pathOrObject: Optional[defcon.Point] = None) -> None: + if pathOrObject is None and self.wrapClass is not None: + pathOrObject = self.wrapClass((0, 0)) + super(RPoint, self)._init(pathOrObject=pathOrObject) - def _postChangeNotification(self): + def _postChangeNotification(self) -> None: contour = self.contour if contour is None: return contour.naked().postNotification("Contour.PointsChanged") self.changed() - def changed(self): + def changed(self) -> None: self.contour.naked().dirty = True + def _getNaked(self) -> defcon.Point: + point = self.naked() + if point is None: + raise ValueError("Point cannot be None.") + return point + # ---------- # Attributes # ---------- # type - def _get_type(self): - value = self.naked().segmentType + def _get_type(self) -> str: + value = self._getNaked().segmentType if value is None: value = "offcurve" return value - def _set_type(self, value): - if value == "offcurve": - value = None - self.naked().segmentType = value + def _set_type(self, value: str) -> None: + self._getNaked().segmentType = None if value == "offcurve" else value self._postChangeNotification() # smooth - def _get_smooth(self): - return self.naked().smooth + def _get_smooth(self) -> bool: + return self._getNaked().smooth - def _set_smooth(self, value): - self.naked().smooth = value + def _set_smooth(self, value: bool) -> None: + self._getNaked().smooth = value self._postChangeNotification() # x - def _get_x(self): - return self.naked().x + def _get_x(self) -> IntFloatType: + return self._getNaked().x - def _set_x(self, value): - self.naked().x = value + def _set_x(self, value: IntFloatType) -> None: + self._getNaked().x = value self._postChangeNotification() # y - def _get_y(self): - return self.naked().y + def _get_y(self) -> IntFloatType: + return self._getNaked().y - def _set_y(self, value): - self.naked().y = value + def _set_y(self, value: IntFloatType) -> None: + self._getNaked().y = value self._postChangeNotification() # -------------- @@ -72,21 +81,20 @@ def _set_y(self, value): # name - def _get_name(self): - return self.naked().name + def _get_name(self) -> Optional[str]: + return self._getNaked().name - def _set_name(self, value): - self.naked().name = value + def _set_name(self, value: str) -> None: + self._getNaked().name = value self._postChangeNotification() # identifier - def _get_identifier(self): - point = self.naked() - return point.identifier + def _get_identifier(self) -> Optional[str]: + return self._getNaked().identifier - def _getIdentifier(self): - point = self.naked() + def _getIdentifier(self) -> str: + point = self._getNaked() value = point.identifier if value is not None: return value From 097656b147651ea7af03aae19404cca94d1ba068 Mon Sep 17 00:00:00 2001 From: knutnergaard <15194233+knutnergaard@users.noreply.github.com> Date: Sun, 16 Mar 2025 15:14:29 +0000 Subject: [PATCH 4/8] Format fixes by ruff --- Lib/fontParts/base/layer.py | 4 +++- Lib/fontParts/fontshell/glyph.py | 2 +- Lib/fontParts/fontshell/guideline.py | 3 +-- Lib/fontParts/fontshell/image.py | 5 ++--- Lib/fontParts/fontshell/layer.py | 4 +--- 5 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Lib/fontParts/base/layer.py b/Lib/fontParts/base/layer.py index 10cd3348..bb7b156d 100644 --- a/Lib/fontParts/base/layer.py +++ b/Lib/fontParts/base/layer.py @@ -822,7 +822,9 @@ def _get_base_color(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: value = Color(value) return value - def _set_base_color(self, value: Optional[QuadrupleCollectionType[IntFloatType]]) -> None: + def _set_base_color( + self, value: Optional[QuadrupleCollectionType[IntFloatType]] + ) -> None: if value is not None: value = normalizers.normalizeColor(value) self._set_color(value) diff --git a/Lib/fontParts/fontshell/glyph.py b/Lib/fontParts/fontshell/glyph.py index b2e51b23..7c897114 100644 --- a/Lib/fontParts/fontshell/glyph.py +++ b/Lib/fontParts/fontshell/glyph.py @@ -327,7 +327,7 @@ def _get_markColor(self) -> Optional[QuadrupleType[float]]: return value def _set_markColor( - self, value: Optional[QuadrupleCollectionType[IntFloatType]] + self, value: Optional[QuadrupleCollectionType[IntFloatType]] ) -> None: self._getNaked().markColor = value diff --git a/Lib/fontParts/fontshell/guideline.py b/Lib/fontParts/fontshell/guideline.py index 1184ad28..c4e4794a 100644 --- a/Lib/fontParts/fontshell/guideline.py +++ b/Lib/fontParts/fontshell/guideline.py @@ -90,7 +90,6 @@ def _get_color(self) -> Optional[QuadrupleType[float]]: return value def _set_color( - self, - value: Optional[QuadrupleCollectionType[IntFloatType]] + self, value: Optional[QuadrupleCollectionType[IntFloatType]] ) -> None: self._getNaked().color = value diff --git a/Lib/fontParts/fontshell/image.py b/Lib/fontParts/fontshell/image.py index fd257d42..9fe5645d 100644 --- a/Lib/fontParts/fontshell/image.py +++ b/Lib/fontParts/fontshell/image.py @@ -9,7 +9,7 @@ SextupleType, QuadrupleCollectionType, SextupleCollectionType, - IntFloatType + IntFloatType, ) from fontParts.fontshell.base import RBaseObject @@ -49,8 +49,7 @@ def _get_color(self) -> Optional[QuadrupleType[float]]: return value def _set_color( - self, - value: Optional[QuadrupleCollectionType[IntFloatType]] + self, value: Optional[QuadrupleCollectionType[IntFloatType]] ) -> None: if self.font is None: self._orphanColor = value diff --git a/Lib/fontParts/fontshell/layer.py b/Lib/fontParts/fontshell/layer.py index dd4fd98f..6c192242 100644 --- a/Lib/fontParts/fontshell/layer.py +++ b/Lib/fontParts/fontshell/layer.py @@ -58,9 +58,7 @@ def _get_color(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: return value def _set_color( - self, - value: Optional[QuadrupleCollectionType[IntFloatType]], - **kwargs: Any + self, value: Optional[QuadrupleCollectionType[IntFloatType]], **kwargs: Any ) -> None: self._getNaked().color = value From 7e5cf2726084f6d267d89aa1761b4fe7a19a14f8 Mon Sep 17 00:00:00 2001 From: knutnergaard Date: Mon, 17 Mar 2025 01:33:05 +0100 Subject: [PATCH 5/8] Correct line breaks. --- Lib/fontParts/base/font.py | 6 ++---- Lib/fontParts/base/glyph.py | 15 +++++---------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/Lib/fontParts/base/font.py b/Lib/fontParts/base/font.py index 97aaad54..6ab0c1b3 100644 --- a/Lib/fontParts/base/font.py +++ b/Lib/fontParts/base/font.py @@ -880,8 +880,7 @@ def _get_base_layers(self) -> Tuple[BaseLayer, ...]: self._setFontInLayer(layer) return tuple(layers) - # type: ignore[return] - def _get_layers(self, **kwargs: Any) -> Tuple[BaseLayer, ...]: + def _get_layers(self, **kwargs: Any) -> Tuple[BaseLayer, ...]: # type: ignore[return] r"""Get the native font's layer objects. This is the environment implementation of @@ -1752,8 +1751,7 @@ def _getitem__guidelines(self, index: int) -> BaseGuideline: self._setFontInGuideline(guideline) return guideline - # type: ignore[return] - def _getGuideline(self, index: int, **kwargs: Any) -> BaseGuideline: + def _getGuideline(self, index: int, **kwargs: Any) -> BaseGuideline: # type: ignore[return] r"""Return the guideline at the given index. :param index: The index of the guideline. diff --git a/Lib/fontParts/base/glyph.py b/Lib/fontParts/base/glyph.py index 32a6b2ad..86736b52 100644 --- a/Lib/fontParts/base/glyph.py +++ b/Lib/fontParts/base/glyph.py @@ -1218,8 +1218,7 @@ def __getitem__(self, index: int) -> BaseContour: self._setGlyphInContour(contour) return contour - # type: ignore[return] - def _getContour(self, index: int, **kwargs: Any) -> BaseContour: + def _getContour(self, index: int, **kwargs: Any) -> BaseContour: # type: ignore[return] r"""Get the contour located at the given index from the native glyph. :param index: The index of the contour to return as an :class:`int`. @@ -1455,8 +1454,7 @@ def _getitem__components(self, index: int) -> BaseComponent: self._setGlyphInComponent(component) return component - # type: ignore[return] - def _getComponent(self, index: int, **kwargs: Any) -> BaseComponent: + def _getComponent(self, index: int, **kwargs: Any) -> BaseComponent: # type: ignore[return] r"""Get the component at the given index from the native glyph. :param index: The index of the component to return as an :class:`int`. @@ -1732,8 +1730,7 @@ def _getitem__anchors(self, index: int) -> BaseAnchor: self._setGlyphInAnchor(anchor) return anchor - # type: ignore[return] - def _getAnchor(self, index: int, **kwargs: Any) -> BaseAnchor: + def _getAnchor(self, index: int, **kwargs: Any) -> BaseAnchor: # type: ignore[return] r"""Get the anchor at the given index from the native glyph. :param index: The index of the anchor to get as an :class:`int`. @@ -1979,8 +1976,7 @@ def _getitem__guidelines(self, index: int) -> BaseGuideline: self._setGlyphInGuideline(guideline) return guideline - # type: ignore[return] - def _getGuideline(self, index: int, **kwargs: Any) -> BaseGuideline: + def _getGuideline(self, index: int, **kwargs: Any) -> BaseGuideline: # type: ignore[return] r"""Get the anchor at the given index from the native glyph. :param index: The index of the guideline to get as an :class:`int`. @@ -3420,8 +3416,7 @@ def _set_base_markColor( value = normalizers.normalizeColor(value) self._set_markColor(value) - # type: ignore[return] - def _get_markColor(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: + def _get_markColor(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: # type: ignore[return] """Get the glyph's mark color. This is the environment implementation of From 0c555a3e58ada9732a14daff672e7900bdbffa8c Mon Sep 17 00:00:00 2001 From: knutnergaard Date: Mon, 17 Mar 2025 09:25:29 +0100 Subject: [PATCH 6/8] Annotate more modules. --- Lib/fontParts/fontshell/info.py | 7 ++++-- Lib/fontParts/fontshell/kerning.py | 35 ++++++++++++++++++++---------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Lib/fontParts/fontshell/info.py b/Lib/fontParts/fontshell/info.py index fa988865..37f4f6ee 100644 --- a/Lib/fontParts/fontshell/info.py +++ b/Lib/fontParts/fontshell/info.py @@ -1,3 +1,6 @@ +from __future__ import annotations +from typing import Any + import defcon from fontParts.base import BaseInfo from fontParts.fontshell.base import RBaseObject @@ -6,8 +9,8 @@ class RInfo(RBaseObject, BaseInfo): wrapClass = defcon.Info - def _getAttr(self, attr): + def _getAttr(self, attr: str) -> Any: return getattr(self.naked(), attr) - def _setAttr(self, attr, value): + def _setAttr(self, attr: str, value: Any) -> None: setattr(self.naked(), attr, value) diff --git a/Lib/fontParts/fontshell/kerning.py b/Lib/fontParts/fontshell/kerning.py index 080b7c5e..bf2b747e 100644 --- a/Lib/fontParts/fontshell/kerning.py +++ b/Lib/fontParts/fontshell/kerning.py @@ -1,4 +1,9 @@ +from __future__ import annotations +from typing import Any +from collections.abc import ItemsView + import defcon +from fontParts.base.annotations import PairCollectionType from fontParts.base import BaseKerning from fontParts.fontshell.base import RBaseObject @@ -6,20 +11,26 @@ class RKerning(RBaseObject, BaseKerning): wrapClass = defcon.Kerning - def _items(self): - return self.naked().items() + def _getNaked(self) -> defcon.Kerning: + kerning = self.naked() + if kerning is None: + raise ValueError("Kerning cannot be None.") + return kerning + + def _items(self) -> ItemsView[str, int]: + return self._getNaked().items() - def _contains(self, key): - return key in self.naked() + def _contains(self, key: str) -> bool: + return key in self._getNaked() - def _setItem(self, key, value): - self.naked()[key] = value + def _setItem(self, key: str, value: int) -> None: + self._getNaked()[key] = value - def _getItem(self, key): - return self.naked()[key] + def _getItem(self, key: str) -> Any: + return self._getNaked()[key] - def _delItem(self, key): - del self.naked()[key] + def _delItem(self, key: str) -> None: + del self._getNaked()[key] - def _find(self, pair, default=0): - return self.naked().find(pair, default) + def _find(self, pair: PairCollectionType[str], default: int = 0) -> int: + return self._getNaked().find(pair, default) From 56e187ab65571c95f8d5e55fc7dc93144d06afad Mon Sep 17 00:00:00 2001 From: knutnergaard Date: Sat, 31 May 2025 16:46:47 +0200 Subject: [PATCH 7/8] giMerge branch 'v1' into lib-value --- Lib/fontParts/fontshell/layer.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Lib/fontParts/fontshell/layer.py b/Lib/fontParts/fontshell/layer.py index 6c192242..6814042f 100644 --- a/Lib/fontParts/fontshell/layer.py +++ b/Lib/fontParts/fontshell/layer.py @@ -3,10 +3,7 @@ import defcon from fontParts.base import BaseLayer -from fontParts.base.annotations import ( - QuadrupleCollectionType, - IntFloatType, -) +from fontParts.base.annotations import QuadrupleCollectionType, IntFloatType from fontParts.fontshell.base import RBaseObject from fontParts.fontshell.lib import RLib from fontParts.fontshell.glyph import RGlyph From fdf8af607ef1f5fd64f2f4701c9134a50be2d4ca Mon Sep 17 00:00:00 2001 From: knutnergaard Date: Sun, 1 Jun 2025 09:22:05 +0200 Subject: [PATCH 8/8] Replace `getNaked` function with `type: ignore` in `naked`. - Various other fixes. --- Lib/fontParts/base/font.py | 4 +- Lib/fontParts/fontshell/anchor.py | 30 ++++----- Lib/fontParts/fontshell/base.py | 12 ++-- Lib/fontParts/fontshell/contour.py | 36 +++++----- Lib/fontParts/fontshell/features.py | 10 +-- Lib/fontParts/fontshell/font.py | 48 ++++++-------- Lib/fontParts/fontshell/glyph.py | 98 +++++++++++++--------------- Lib/fontParts/fontshell/groups.py | 20 ++---- Lib/fontParts/fontshell/guideline.py | 32 ++++----- Lib/fontParts/fontshell/image.py | 18 ++--- Lib/fontParts/fontshell/kerning.py | 18 ++--- Lib/fontParts/fontshell/layer.py | 37 +++++------ Lib/fontParts/fontshell/lib.py | 16 ++--- Lib/fontParts/fontshell/point.py | 30 ++++----- Lib/fontParts/test/test_font.py | 2 +- Lib/fontParts/test/test_groups.py | 20 +++--- 16 files changed, 180 insertions(+), 251 deletions(-) diff --git a/Lib/fontParts/base/font.py b/Lib/fontParts/base/font.py index 964f4d45..53016c8a 100644 --- a/Lib/fontParts/base/font.py +++ b/Lib/fontParts/base/font.py @@ -131,8 +131,8 @@ def copyData(self, source: BaseFont) -> None: else: layer = self.newLayer(layerName) layer.copyData(source.getLayer(layerName)) - for guideline in self.guidelines: - self.appendGuideline(guideline) + for guideline in source.guidelines: + self.appendGuideline(guideline=guideline) super(BaseFont, self).copyData(source) # --------------- diff --git a/Lib/fontParts/fontshell/anchor.py b/Lib/fontParts/fontshell/anchor.py index b415763b..b5694260 100644 --- a/Lib/fontParts/fontshell/anchor.py +++ b/Lib/fontParts/fontshell/anchor.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Optional +from typing import cast, Optional import defcon from fontParts.base import BaseAnchor @@ -22,12 +22,6 @@ def _init(self, pathOrObject: Optional[defcon.Anchor] = None) -> None: pathOrObject.y = 0 super(RAnchor, self)._init(pathOrObject=pathOrObject) - def _getNaked(self) -> defcon.Anchor: - anchor = self.naked() - if anchor is None: - raise ValueError("Anchor cannot be None.") - return anchor - # -------- # Position # -------- @@ -35,18 +29,18 @@ def _getNaked(self) -> defcon.Anchor: # x def _get_x(self) -> float: - return self._getNaked().x + return self.naked().x def _set_x(self, value: float) -> None: - self._getNaked().x = value + self.naked().x = value # y def _get_y(self) -> float: - return self._getNaked().y + return self.naked().y def _set_y(self, value: float) -> None: - self._getNaked().y = value + self.naked().y = value # -------------- # Identification @@ -55,28 +49,28 @@ def _set_y(self, value: float) -> None: # identifier def _get_identifier(self) -> Optional[str]: - return self._getNaked().identifier + return self.naked().identifier def _getIdentifier(self) -> str: - return self._getNaked().generateIdentifier() + return self.naked().generateIdentifier() def _setIdentifier(self, value: str) -> None: - self._getNaked().identifier = value + self.naked().identifier = value # name def _get_name(self) -> Optional[str]: - return self._getNaked().name + return self.naked().name def _set_name(self, value: str) -> None: - self._getNaked().name = value + self.naked().name = value # color def _get_color(self) -> Optional[QuadrupleType[float]]: - return self._getNaked().color + return self.naked().color def _set_color( self, value: Optional[QuadrupleCollectionType[IntFloatType]] ) -> None: - self._getNaked().color = value + self.naked().color = value diff --git a/Lib/fontParts/fontshell/base.py b/Lib/fontParts/fontshell/base.py index 276a1581..00f9244a 100644 --- a/Lib/fontParts/fontshell/base.py +++ b/Lib/fontParts/fontshell/base.py @@ -9,17 +9,15 @@ class RBaseObject(Generic[RBaseObjectType]): dirty: bool def _init(self, pathOrObject: Optional[RBaseObjectType] = None) -> None: + if pathOrObject is None and self.wrapClass is not None: + pathOrObject = self.wrapClass() # pylint: disable=E1102 if pathOrObject is not None: self._wrapped = pathOrObject - if self.wrapClass is not None: - pathOrObject = self.wrapClass() def changed(self) -> None: - naked = self.naked() - if naked is not None: - naked.dirty = True + self.naked().dirty = True - def naked(self) -> Optional[RBaseObjectType]: + def naked(self) -> RBaseObjectType: if hasattr(self, "_wrapped"): return self._wrapped - return None + return None # type: ignore[return-value] diff --git a/Lib/fontParts/fontshell/contour.py b/Lib/fontParts/fontshell/contour.py index 46be715b..2952dd7f 100644 --- a/Lib/fontParts/fontshell/contour.py +++ b/Lib/fontParts/fontshell/contour.py @@ -19,12 +19,6 @@ class RContour(RBaseObject, BaseContour): segmentClass = RSegment bPointClass = RBPoint - def _getNaked(self) -> defcon.Contour: - contour = self.naked() - if contour is None: - raise ValueError("Contour cannot be None.") - return contour - # -------------- # Identification # -------------- @@ -32,7 +26,7 @@ def _getNaked(self) -> defcon.Contour: # index def _set_index(self, value: int) -> None: - contour = self._getNaked() + contour = self.naked() glyph = contour.glyph if glyph is not None: glyph.removeContour(contour) @@ -41,13 +35,13 @@ def _set_index(self, value: int) -> None: # identifier def _get_identifier(self) -> Optional[str]: - return self._getNaked().identifier + return self.naked().identifier def _getIdentifier(self) -> str: - return self._getNaked().generateIdentifier() + return self.naked().generateIdentifier() def _getIdentifierForPoint(self, point: BasePoint) -> str: - contour = self._getNaked() + contour = self.naked() nakedPoint = point.naked() return contour.generateIdentifierForPoint(nakedPoint) @@ -56,51 +50,51 @@ def _getIdentifierForPoint(self, point: BasePoint) -> str: # ---- def _get_open(self) -> bool: - return self._getNaked().open + return self.naked().open # ------ # Bounds # ------ def _get_bounds(self) -> Optional[QuadrupleType[float]]: - return self._getNaked().bounds + return self.naked().bounds # ---- # Area # ---- def _get_area(self) -> Optional[float]: - return self._getNaked().area + return self.naked().area # --------- # Direction # --------- def _get_clockwise(self) -> bool: - return self._getNaked().clockwise + return self.naked().clockwise def _reverse(self, **kwargs: Any) -> None: - self._getNaked().reverse() + self.naked().reverse() # ------------------------ # Point and Contour Inside # ------------------------ def _pointInside(self, point: PairCollectionType[IntFloatType]) -> bool: - return self._getNaked().pointInside(point) + return self.naked().pointInside(point) def _contourInside(self, otherContour: BaseContour) -> bool: - return self._getNaked().contourInside(otherContour.naked(), segmentLength=5) + return self.naked().contourInside(otherContour.naked(), segmentLength=5) # ------ # Points # ------ def _lenPoints(self, **kwargs: Any) -> int: - return len(self._getNaked()) + return len(self.naked()) def _getPoint(self, index: int, **kwargs: Any) -> RPoint: - contour = self._getNaked() + contour = self.naked() point = contour[index] return self.pointClass(point) @@ -124,10 +118,10 @@ def _insertPoint( if nakedPoint is not None: point = nakedPoint point.identifier = identifier - contour = self._getNaked() + contour = self.naked() contour.insertPoint(index, point) def _removePoint(self, index: int, preserveCurve: bool, **kwargs: Any) -> None: - contour = self._getNaked() + contour = self.naked() point = contour[index] contour.removePoint(point) diff --git a/Lib/fontParts/fontshell/features.py b/Lib/fontParts/fontshell/features.py index 05f5cf5c..0dc12ad2 100644 --- a/Lib/fontParts/fontshell/features.py +++ b/Lib/fontParts/fontshell/features.py @@ -9,16 +9,10 @@ class RFeatures(RBaseObject, BaseFeatures): wrapClass = defcon.Features - def _getNaked(self) -> defcon.Features: - freatures = self.naked() - if freatures is None: - raise ValueError("Features cannot be None.") - return freatures - def _get_text(self) -> Optional[str]: - features = self._getNaked() + features = self.naked() return features.text def _set_text(self, value: str) -> None: - features = self._getNaked() + features = self.naked() features.text = value diff --git a/Lib/fontParts/fontshell/font.py b/Lib/fontParts/fontshell/font.py index 2cc743c1..3e9d3841 100644 --- a/Lib/fontParts/fontshell/font.py +++ b/Lib/fontParts/fontshell/font.py @@ -30,12 +30,6 @@ class RFont(RBaseObject, BaseFont): layerClass = RLayer guidelineClass = RGuideline - def _getNaked(self) -> defcon.Font: - font = self.naked() - if font is None: - raise ValueError("Font cannot be None.") - return font - # --------------- # File Operations # --------------- @@ -60,7 +54,7 @@ def _init( # path def _get_path(self, **kwargs: Any) -> Optional[str]: - return self._getNaked().path + return self.naked().path # save @@ -72,7 +66,7 @@ def _save( fileStructure: Optional[str] = None, **kwargs: Any, ) -> None: - self._getNaked().save( + self.naked().save( path=path, formatVersion=formatVersion, structure=fileStructure ) @@ -88,32 +82,32 @@ def _close(self, **kwargs: Any) -> None: # info def _get_info(self) -> RInfo: - return self.infoClass(pathOrObject=self._getNaked().info) + return self.infoClass(pathOrObject=self.naked().info) # groups def _get_groups(self) -> RGroups: - return self.groupsClass(pathOrObject=self._getNaked().groups) + return self.groupsClass(pathOrObject=self.naked().groups) # kerning def _get_kerning(self) -> RKerning: - return self.kerningClass(pathOrObject=self._getNaked().kerning) + return self.kerningClass(pathOrObject=self.naked().kerning) # features def _get_features(self) -> RFeatures: - return self.featuresClass(pathOrObject=self._getNaked().features) + return self.featuresClass(pathOrObject=self.naked().features) # lib def _get_lib(self) -> RLib: - return self.libClass(pathOrObject=self._getNaked().lib) + return self.libClass(pathOrObject=self.naked().lib) # tempLib def _get_tempLib(self) -> RLib: - return self.libClass(pathOrObject=self._getNaked().tempLib) + return self.libClass(pathOrObject=self.naked().tempLib) # ------ # Layers @@ -121,24 +115,24 @@ def _get_tempLib(self) -> RLib: def _get_layers(self, **kwargs: Any) -> Tuple[RLayer, ...]: return tuple( - self.layerClass(pathOrObject=layer) for layer in self._getNaked().layers + self.layerClass(pathOrObject=layer) for layer in self.naked().layers ) # order def _get_layerOrder(self, **kwargs: Any) -> Tuple[str, ...]: - return self._getNaked().layers.layerOrder + return self.naked().layers.layerOrder def _set_layerOrder(self, value: CollectionType[str], **kwargs: Any) -> None: - self._getNaked().layers.layerOrder = value + self.naked().layers.layerOrder = value # default layer def _get_defaultLayerName(self) -> str: - return self._getNaked().layers.defaultLayer.name + return self.naked().layers.defaultLayer.name def _set_defaultLayerName(self, value: str, **kwargs: Any) -> None: - font = self._getNaked() + font = self.naked() for layer in self.layers: if layer.name == value: break @@ -153,7 +147,7 @@ def _newLayer( color: Optional[QuadrupleCollectionType[IntFloatType]], **kwargs: Any, ) -> RLayer: - layers = self._getNaked().layers + layers = self.naked().layers layer = layers.newLayer(name) layer.color = color return self.layerClass(pathOrObject=layer) @@ -161,7 +155,7 @@ def _newLayer( # remove def _removeLayer(self, name: str, **kwargs: Any) -> None: - layers = self._getNaked().layers + layers = self.naked().layers del layers[name] # ------ @@ -169,20 +163,20 @@ def _removeLayer(self, name: str, **kwargs: Any) -> None: # ------ def _get_glyphOrder(self) -> Tuple[str, ...]: - return self._getNaked().glyphOrder + return self.naked().glyphOrder def _set_glyphOrder(self, value: CollectionType[str]) -> None: - self._getNaked().glyphOrder = value + self.naked().glyphOrder = value # ---------- # Guidelines # ---------- def _lenGuidelines(self, **kwargs: Any) -> int: - return len(self._getNaked().guidelines) + return len(self.naked().guidelines) def _getGuideline(self, index: int, **kwargs: Any) -> RGuideline: - guideline = self._getNaked().guidelines[index] + guideline = self.naked().guidelines[index] return self.guidelineClass(guideline) def _appendGuideline( @@ -194,7 +188,7 @@ def _appendGuideline( identifier: Optional[str] = None, **kwargs: Any, ) -> RGuideline: - font = self._getNaked() + font = self.naked() guideline = self.guidelineClass().naked() if guideline is None: raise ValueError("Guideline cannot be None.") @@ -208,6 +202,6 @@ def _appendGuideline( return self.guidelineClass(guideline) def _removeGuideline(self, index: int, **kwargs: Any) -> None: - font = self._getNaked() + font = self.naked() guideline = font.guidelines[index] font.removeGuideline(guideline) diff --git a/Lib/fontParts/fontshell/glyph.py b/Lib/fontParts/fontshell/glyph.py index 7c897114..b2e4a35e 100644 --- a/Lib/fontParts/fontshell/glyph.py +++ b/Lib/fontParts/fontshell/glyph.py @@ -44,12 +44,6 @@ class RGlyph(RBaseObject, BaseGlyph): imageClass = RImage libClass = RLib - def _getNaked(self) -> defcon.Glyph: - glyph = self.naked() - if glyph is None: - raise ValueError("Glyph cannot be None.") - return glyph - # -------------- # Identification # -------------- @@ -57,18 +51,18 @@ def _getNaked(self) -> defcon.Glyph: # Name def _get_name(self) -> str: - return self._getNaked().name + return self.naked().name def _set_name(self, value: str) -> None: - self._getNaked().name = value + self.naked().name = value # Unicodes def _get_unicodes(self) -> CollectionType[int]: - return self._getNaked().unicodes + return self.naked().unicodes def _set_unicodes(self, value: CollectionType[int]) -> None: - self._getNaked().unicodes = value + self.naked().unicodes = value # ------- # Metrics @@ -77,66 +71,66 @@ def _set_unicodes(self, value: CollectionType[int]) -> None: # horizontal def _get_width(self) -> IntFloatType: - return self._getNaked().width + return self.naked().width def _set_width(self, value: IntFloatType) -> None: - self._getNaked().width = value + self.naked().width = value def _get_leftMargin(self) -> Optional[IntFloatType]: - return self._getNaked().leftMargin + return self.naked().leftMargin def _set_leftMargin(self, value: IntFloatType) -> None: - self._getNaked().leftMargin = value + self.naked().leftMargin = value def _get_rightMargin(self) -> Optional[IntFloatType]: - return self._getNaked().rightMargin + return self.naked().rightMargin def _set_rightMargin(self, value: IntFloatType) -> None: - self._getNaked().rightMargin = value + self.naked().rightMargin = value # vertical def _get_height(self) -> IntFloatType: - return self._getNaked().height + return self.naked().height def _set_height(self, value: IntFloatType) -> None: - self._getNaked().height = value + self.naked().height = value def _get_bottomMargin(self) -> Optional[IntFloatType]: - return self._getNaked().bottomMargin + return self.naked().bottomMargin def _set_bottomMargin(self, value: IntFloatType) -> None: - self._getNaked().bottomMargin = value + self.naked().bottomMargin = value def _get_topMargin(self) -> Optional[IntFloatType]: - return self._getNaked().topMargin + return self.naked().topMargin def _set_topMargin(self, value: IntFloatType) -> None: - self._getNaked().topMargin = value + self.naked().topMargin = value # ------ # Bounds # ------ def _get_bounds(self) -> Optional[QuadrupleType[IntFloatType]]: - return self._getNaked().bounds + return self.naked().bounds # ---- # Area # ---- def _get_area(self) -> Optional[float]: - return self._getNaked().area + return self.naked().area # ---- # Pens # ---- def getPen(self) -> SegmentToPointPen: - return self._getNaked().getPen() + return self.naked().getPen() def getPointPen(self) -> Union[GlyphObjectPointPen, GlyphObjectLoadingPointPen]: - return self._getNaked().getPointPen() + return self.naked().getPointPen() # ----------------------------------------- # Contour, Component and Anchor Interaction @@ -145,14 +139,14 @@ def getPointPen(self) -> Union[GlyphObjectPointPen, GlyphObjectLoadingPointPen]: # Contours def _lenContours(self, **kwargs: Any) -> int: - return len(self._getNaked()) + return len(self.naked()) def _getContour(self, index: int, **kwargs: Any) -> RContour: - contour = self._getNaked()[index] + contour = self.naked()[index] return self.contourClass(contour) def _removeContour(self, index: int, **kwargs: Any) -> None: - glyph = self._getNaked() + glyph = self.naked() contour = glyph[index] glyph.removeContour(contour) @@ -173,29 +167,29 @@ def _removeOverlap(self, **kwargs: Any) -> None: booleanOperations.union(contours, self.getPointPen()) def _correctDirection(self, trueType: bool = False, **kwargs: Any) -> None: - self._getNaked().correctContourDirection(trueType=trueType) + self.naked().correctContourDirection(trueType=trueType) # Components def _lenComponents(self, **kwargs: Any) -> int: - return len(self._getNaked().components) + return len(self.naked().components) def _getComponent(self, index: int, **kwargs: Any) -> RComponent: - component = self._getNaked().components[index] + component = self.naked().components[index] return self.componentClass(component) def _removeComponent(self, index: int, **kwargs: Any) -> None: - glyph = self._getNaked() + glyph = self.naked() component = glyph.components[index] glyph.removeComponent(component) # Anchors def _lenAnchors(self, **kwargs: Any) -> int: - return len(self._getNaked().anchors) + return len(self.naked().anchors) def _getAnchor(self, index: int, **kwargs: Any) -> RAnchor: - anchor = self._getNaked().anchors[index] + anchor = self.naked().anchors[index] return self.anchorClass(anchor) def _appendAnchor( @@ -206,7 +200,7 @@ def _appendAnchor( identifier: Optional[str] = None, **kwargs: Any, ) -> RAnchor: - glyph = self._getNaked() + glyph = self.naked() anchor = self.anchorClass().naked() if anchor is None: raise ValueError("Anchor cannot be None") @@ -222,17 +216,17 @@ def _appendAnchor( return wrapped def _removeAnchor(self, index: int, **kwargs: Any) -> None: - glyph = self._getNaked() + glyph = self.naked() anchor = glyph.anchors[index] glyph.removeAnchor(anchor) # Guidelines def _lenGuidelines(self, **kwargs: Any) -> int: - return len(self._getNaked().guidelines) + return len(self.naked().guidelines) def _getGuideline(self, index: int, **kwargs: Any) -> RGuideline: - guideline = self._getNaked().guidelines[index] + guideline = self.naked().guidelines[index] return self.guidelineClass(guideline) def _appendGuideline( @@ -244,7 +238,7 @@ def _appendGuideline( identifier: Optional[str] = None, **kwargs: Any, ) -> RGuideline: - glyph = self._getNaked() + glyph = self.naked() guideline = self.guidelineClass().naked() if guideline is None: raise ValueError("Guideline cannot be None") @@ -258,7 +252,7 @@ def _appendGuideline( return self.guidelineClass(guideline) def _removeGuideline(self, index: int, **kwargs: Any) -> None: - glyph = self._getNaked() + glyph = self.naked() guideline = glyph.guidelines[index] glyph.removeGuideline(guideline) @@ -293,7 +287,7 @@ def _removeLayer(self, name: str, **kwargs: Any) -> None: # ----- def _get_image(self) -> Optional[RImage]: - image = self._getNaked().image + image = self.naked().image if image is None: return None return self.imageClass(image) @@ -304,7 +298,7 @@ def _addImage( transformation: Optional[SextupleCollectionType[IntFloatType]] = None, color: Optional[QuadrupleCollectionType[IntFloatType]] = None, ) -> None: - image = self._getNaked().image + image = self.naked().image image = self.imageClass(image) image.glyph = self image.data = data @@ -312,7 +306,7 @@ def _addImage( image.color = color def _clearImage(self, **kwargs: Any) -> None: - self._getNaked().image = None + self.naked().image = None # ---- # Note @@ -321,7 +315,7 @@ def _clearImage(self, **kwargs: Any) -> None: # Mark def _get_markColor(self) -> Optional[QuadrupleType[float]]: - value = self._getNaked().markColor + value = self.naked().markColor if value is not None: value = tuple(value) return value @@ -329,15 +323,15 @@ def _get_markColor(self) -> Optional[QuadrupleType[float]]: def _set_markColor( self, value: Optional[QuadrupleCollectionType[IntFloatType]] ) -> None: - self._getNaked().markColor = value + self.naked().markColor = value # Note def _get_note(self) -> Optional[str]: - return self._getNaked().note + return self.naked().note def _set_note(self, value: Optional[str]) -> None: - self._getNaked().note = value + self.naked().note = value # ----------- # Sub-Objects @@ -346,12 +340,12 @@ def _set_note(self, value: Optional[str]) -> None: # lib def _get_lib(self) -> RLib: - return self.libClass(wrap=self._getNaked().lib) + return self.libClass(pathOrObject=self.naked().lib) # tempLib def _get_tempLib(self) -> RLib: - return self.libClass(wrap=self._getNaked().tempLib) + return self.libClass(pathOrObject=self.naked().tempLib) # --- # API @@ -361,7 +355,7 @@ def _loadFromGLIF(self, glifData: str, validate: bool = True) -> None: try: readGlyphFromString( aString=glifData, - glyphObject=self._getNaked(), + glyphObject=self.naked(), pointPen=self.getPointPen(), validate=validate, ) @@ -369,7 +363,7 @@ def _loadFromGLIF(self, glifData: str, validate: bool = True) -> None: raise FontPartsError("Not valid glif data") from e def _dumpToGLIF(self, glyphFormatVersion: int) -> str: - glyph = self._getNaked() + glyph = self.naked() return writeGlyphToString( glyphName=glyph.name, glyphObject=glyph, diff --git a/Lib/fontParts/fontshell/groups.py b/Lib/fontParts/fontshell/groups.py index 9271cd62..59e121c5 100644 --- a/Lib/fontParts/fontshell/groups.py +++ b/Lib/fontParts/fontshell/groups.py @@ -13,39 +13,33 @@ class RGroups(RBaseObject, BaseGroups): wrapClass = defcon.Groups - def _getNaked(self) -> defcon.Groups: - groups = self.naked() - if groups is None: - raise ValueError("Groups cannot be None.") - return groups - def _get_side1KerningGroups(self) -> GroupsDict: - groups = self._getNaked() + groups = self.naked() representation = groups.getRepresentation("defcon.groups.kerningSide1Groups") return {k: tuple(v) for k, v in representation.items()} def _get_side2KerningGroups(self) -> GroupsDict: - groups = self._getNaked() + groups = self.naked() representation = groups.getRepresentation("defcon.groups.kerningSide2Groups") return {k: tuple(v) for k, v in representation.items()} def _items(self) -> ItemsView[str, Tuple[str]]: - groups = self._getNaked() + groups = self.naked() formatted = {k: tuple(v) for k, v in groups.items()} return formatted.items() def _contains(self, key: str) -> bool: - groups = self._getNaked() + groups = self.naked() return key in groups def _setItem(self, key: str, value: CollectionType[str]) -> None: - groups = self._getNaked() + groups = self.naked() groups[key] = tuple(value) def _getItem(self, key: str) -> Tuple[str]: - groups = self._getNaked() + groups = self.naked() return tuple(groups[key]) def _delItem(self, key: str) -> None: - groups = self._getNaked() + groups = self.naked() del groups[key] diff --git a/Lib/fontParts/fontshell/guideline.py b/Lib/fontParts/fontshell/guideline.py index c4e4794a..fe8387e5 100644 --- a/Lib/fontParts/fontshell/guideline.py +++ b/Lib/fontParts/fontshell/guideline.py @@ -24,12 +24,6 @@ def _init(self, pathOrObject: Optional[defcon.Guideline] = None) -> None: pathOrObject.angle = 0 super(RGuideline, self)._init(pathOrObject=pathOrObject) - def _getNaked(self) -> defcon.Guideline: - guideline = self.naked() - if guideline is None: - raise ValueError("Guideline cannot be None.") - return guideline - # -------- # Position # -------- @@ -37,26 +31,26 @@ def _getNaked(self) -> defcon.Guideline: # x def _get_x(self) -> float: - return self._getNaked().x + return self.naked().x def _set_x(self, value: float) -> None: - self._getNaked().x = value + self.naked().x = value # y def _get_y(self) -> float: - return self._getNaked().y + return self.naked().y def _set_y(self, value: float) -> None: - self._getNaked().y = value + self.naked().y = value # angle def _get_angle(self) -> float: - return self._getNaked().angle + return self.naked().angle def _set_angle(self, value: Optional[IntFloatType]) -> None: - self._getNaked().angle = value + self.naked().angle = value # -------------- # Identification @@ -65,26 +59,26 @@ def _set_angle(self, value: Optional[IntFloatType]) -> None: # identifier def _get_identifier(self) -> Optional[str]: - return self._getNaked().identifier + return self.naked().identifier def _getIdentifier(self) -> str: - return self._getNaked().generateIdentifier() + return self.naked().generateIdentifier() def _setIdentifier(self, value: str) -> None: - self._getNaked().identifier = value + self.naked().identifier = value # name def _get_name(self) -> Optional[str]: - return self._getNaked().name + return self.naked().name def _set_name(self, value: Optional[str]) -> None: - self._getNaked().name = value + self.naked().name = value # color def _get_color(self) -> Optional[QuadrupleType[float]]: - value = self._getNaked().color + value = self.naked().color if value is not None: value = tuple(value) return value @@ -92,4 +86,4 @@ def _get_color(self) -> Optional[QuadrupleType[float]]: def _set_color( self, value: Optional[QuadrupleCollectionType[IntFloatType]] ) -> None: - self._getNaked().color = value + self.naked().color = value diff --git a/Lib/fontParts/fontshell/image.py b/Lib/fontParts/fontshell/image.py index 9fe5645d..889be55e 100644 --- a/Lib/fontParts/fontshell/image.py +++ b/Lib/fontParts/fontshell/image.py @@ -19,12 +19,6 @@ class RImage(RBaseObject, BaseImage): _orphanData: Optional[bytes] = None _orphanColor: Optional[QuadrupleCollectionType[IntFloatType]] = None - def _getNaked(self) -> defcon.Image: - image = self.naked() - if image is None: - raise ValueError("Image cannot be None.") - return image - # ---------- # Attributes # ---------- @@ -32,10 +26,10 @@ def _getNaked(self) -> defcon.Image: # Transformation def _get_transformation(self) -> SextupleType[float]: - return self._getNaked().transformation + return self.naked().transformation def _set_transformation(self, value: SextupleCollectionType[float]) -> None: - self._getNaked().transformation = value + self.naked().transformation = value # Color @@ -43,7 +37,7 @@ def _get_color(self) -> Optional[QuadrupleType[float]]: if self.font is None and self._orphanColor is not None: r, g, b, a = self._orphanColor return (r, g, b, a) - value = self._getNaked().color + value = self.naked().color if value is not None: value = tuple(value) return value @@ -54,14 +48,14 @@ def _set_color( if self.font is None: self._orphanColor = value else: - self._getNaked().color = value + self.naked().color = value # Data def _get_data(self) -> Optional[bytes]: if self.font is None: return self._orphanData - image = self._getNaked() + image = self.naked() images = self.font.naked().images fileName = image.fileName if fileName is None: @@ -78,7 +72,7 @@ def _set_data(self, value: bytes) -> None: if self.font is None: self._orphanData = value else: - image = self._getNaked() + image = self.naked() images = image.font.images fileName = images.findDuplicateImage(value) if fileName is None: diff --git a/Lib/fontParts/fontshell/kerning.py b/Lib/fontParts/fontshell/kerning.py index bf2b747e..9b0b36b8 100644 --- a/Lib/fontParts/fontshell/kerning.py +++ b/Lib/fontParts/fontshell/kerning.py @@ -11,26 +11,20 @@ class RKerning(RBaseObject, BaseKerning): wrapClass = defcon.Kerning - def _getNaked(self) -> defcon.Kerning: - kerning = self.naked() - if kerning is None: - raise ValueError("Kerning cannot be None.") - return kerning - def _items(self) -> ItemsView[str, int]: - return self._getNaked().items() + return self.naked().items() def _contains(self, key: str) -> bool: - return key in self._getNaked() + return key in self.naked() def _setItem(self, key: str, value: int) -> None: - self._getNaked()[key] = value + self.naked()[key] = value def _getItem(self, key: str) -> Any: - return self._getNaked()[key] + return self.naked()[key] def _delItem(self, key: str) -> None: - del self._getNaked()[key] + del self.naked()[key] def _find(self, pair: PairCollectionType[str], default: int = 0) -> int: - return self._getNaked().find(pair, default) + return self.naked().find(pair, default) diff --git a/Lib/fontParts/fontshell/layer.py b/Lib/fontParts/fontshell/layer.py index 6814042f..fd556ae9 100644 --- a/Lib/fontParts/fontshell/layer.py +++ b/Lib/fontParts/fontshell/layer.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import Optional, Tuple, Dict, Any +from typing import TYPE_CHECKING, Optional, Tuple, Dict, Any import defcon from fontParts.base import BaseLayer @@ -8,18 +8,15 @@ from fontParts.fontshell.lib import RLib from fontParts.fontshell.glyph import RGlyph +if TYPE_CHECKING: + from fontParts.base.glyph import BaseGlyph + class RLayer(RBaseObject, BaseLayer): wrapClass = defcon.Layer libClass = RLib glyphClass = RGlyph - def _getNaked(self) -> defcon.Layer: - layer = self.naked() - if layer is None: - raise ValueError("Layer cannot be None.") - return layer - # ----------- # Sub-Objects # ----------- @@ -27,12 +24,12 @@ def _getNaked(self) -> defcon.Layer: # lib def _get_lib(self) -> RLib: - return self.libClass(wrap=self._getNaked().lib) + return self.libClass(pathOrObject=self.naked().lib) # tempLib def _get_tempLib(self) -> RLib: - return self.libClass(wrap=self._getNaked().tempLib) + return self.libClass(pathOrObject=self.naked().tempLib) # -------------- # Identification @@ -41,15 +38,15 @@ def _get_tempLib(self) -> RLib: # name def _get_name(self) -> str: - return self._getNaked().name + return self.naked().name def _set_name(self, value: str, **kwargs: Any) -> None: - self._getNaked().name = value + self.naked().name = value # color def _get_color(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: - value = self._getNaked().color + value = self.naked().color if value is not None: value = tuple(value) return value @@ -57,27 +54,27 @@ def _get_color(self) -> Optional[QuadrupleCollectionType[IntFloatType]]: def _set_color( self, value: Optional[QuadrupleCollectionType[IntFloatType]], **kwargs: Any ) -> None: - self._getNaked().color = value + self.naked().color = value # ----------------- # Glyph Interaction # ----------------- def _getItem(self, name: str, **kwargs: Any) -> RGlyph: - layer = self._getNaked() + layer = self.naked() glyph = layer[name] return self.glyphClass(glyph) def _keys(self, **kwargs: Any) -> Tuple[str, ...]: - return tuple(self._getNaked().keys()) + return tuple(self.naked().keys()) - def _newGlyph(self, name: str, **kwargs: Any) -> RGlyph: - layer = self._getNaked() + def _newGlyph(self, name: str, **kwargs: Any) -> BaseGlyph: + layer = self.naked() layer.newGlyph(name) return self[name] def _removeGlyph(self, name: str, **kwargs: Any) -> None: - layer = self._getNaked() + layer = self.naked() del layer[name] # ------- @@ -85,9 +82,9 @@ def _removeGlyph(self, name: str, **kwargs: Any) -> None: # ------- def _getReverseComponentMapping(self) -> Dict[str, Tuple[str, ...]]: - mapping = self._getNaked().componentReferences + mapping = self.naked().componentReferences return {k: tuple(v) for k, v in mapping.items()} def _getCharacterMapping(self) -> Dict[int, Tuple[str, ...]]: - mapping = self._getNaked().unicodeData + mapping = self.naked().unicodeData return {k: tuple(v) for k, v in mapping.items()} diff --git a/Lib/fontParts/fontshell/lib.py b/Lib/fontParts/fontshell/lib.py index 1e38eab2..31cd453a 100644 --- a/Lib/fontParts/fontshell/lib.py +++ b/Lib/fontParts/fontshell/lib.py @@ -12,23 +12,17 @@ class RLib(RBaseObject, BaseLib): wrapClass = defcon.Lib - def _getNaked(self) -> defcon.Lib: - lib = self.naked() - if lib is None: - raise ValueError("Lib cannot be None.") - return lib - def _items(self) -> ItemsView[str, Any]: - return self._getNaked().items() + return self.naked().items() def _contains(self, key: str) -> bool: - return key in self._getNaked() + return key in self.naked() def _setItem(self, key: str, value: Any) -> None: - self._getNaked()[key] = value + self.naked()[key] = value def _getItem(self, key: str) -> Any: - return self._getNaked()[key] + return self.naked()[key] def _delItem(self, key: str) -> None: - del self._getNaked()[key] + del self.naked()[key] diff --git a/Lib/fontParts/fontshell/point.py b/Lib/fontParts/fontshell/point.py index f1ccc6cf..cfec069d 100644 --- a/Lib/fontParts/fontshell/point.py +++ b/Lib/fontParts/fontshell/point.py @@ -26,12 +26,6 @@ def _postChangeNotification(self) -> None: def changed(self) -> None: self.contour.naked().dirty = True - def _getNaked(self) -> defcon.Point: - point = self.naked() - if point is None: - raise ValueError("Point cannot be None.") - return point - # ---------- # Attributes # ---------- @@ -39,40 +33,40 @@ def _getNaked(self) -> defcon.Point: # type def _get_type(self) -> str: - value = self._getNaked().segmentType + value = self.naked().segmentType if value is None: value = "offcurve" return value def _set_type(self, value: str) -> None: - self._getNaked().segmentType = None if value == "offcurve" else value + self.naked().segmentType = None if value == "offcurve" else value self._postChangeNotification() # smooth def _get_smooth(self) -> bool: - return self._getNaked().smooth + return self.naked().smooth def _set_smooth(self, value: bool) -> None: - self._getNaked().smooth = value + self.naked().smooth = value self._postChangeNotification() # x def _get_x(self) -> IntFloatType: - return self._getNaked().x + return self.naked().x def _set_x(self, value: IntFloatType) -> None: - self._getNaked().x = value + self.naked().x = value self._postChangeNotification() # y def _get_y(self) -> IntFloatType: - return self._getNaked().y + return self.naked().y def _set_y(self, value: IntFloatType) -> None: - self._getNaked().y = value + self.naked().y = value self._postChangeNotification() # -------------- @@ -82,19 +76,19 @@ def _set_y(self, value: IntFloatType) -> None: # name def _get_name(self) -> Optional[str]: - return self._getNaked().name + return self.naked().name def _set_name(self, value: str) -> None: - self._getNaked().name = value + self.naked().name = value self._postChangeNotification() # identifier def _get_identifier(self) -> Optional[str]: - return self._getNaked().identifier + return self.naked().identifier def _getIdentifier(self) -> str: - point = self._getNaked() + point = self.naked() value = point.identifier if value is not None: return value diff --git a/Lib/fontParts/test/test_font.py b/Lib/fontParts/test/test_font.py index c677c82f..1e20915f 100644 --- a/Lib/fontParts/test/test_font.py +++ b/Lib/fontParts/test/test_font.py @@ -470,7 +470,7 @@ def testCases(path): def test_copy(self): font = self.getFont_glyphs() copy = font.copy() - self.assertEqual(font.keys(), copy.keys()) + self.assertCountEqual(font.keys(), copy.keys()) font = self.getFont_glyphs() font.defaultLayer.name = "hello" diff --git a/Lib/fontParts/test/test_groups.py b/Lib/fontParts/test/test_groups.py index 31f9818b..cd2a3123 100644 --- a/Lib/fontParts/test/test_groups.py +++ b/Lib/fontParts/test/test_groups.py @@ -7,10 +7,10 @@ def getGroups_generic(self): groups, _ = self.objectGenerator("groups") groups.update( { - "group 1": ["A", "B", "C"], - "group 2": ["x", "y", "z"], - "group 3": [], - "group 4": ["A"], + "group 1": ("A", "B", "C"), + "group 2": ("x", "y", "z"), + "group 3": (), + "group 4": ("A",), } ) return groups @@ -147,10 +147,10 @@ def test_get_not_found(self): def getGroups_kerning(self): groups = self.getGroups_generic() kerningGroups = { - "public.kern1.A": ["A", "Aacute"], - "public.kern1.O": ["O", "D"], - "public.kern2.A": ["A", "Aacute"], - "public.kern2.O": ["O", "C"], + "public.kern1.A": ("A", "Aacute"), + "public.kern1.O": ("O", "D"), + "public.kern2.A": ("A", "Aacute"), + "public.kern2.O": ("O", "C"), } groups.update(kerningGroups) return groups @@ -163,7 +163,7 @@ def test_side1KerningGroups(self): def test_get_side1KerningGroups(self): groups = self.getGroups_kerning() - expected = {"public.kern1.A": ["A", "Aacute"], "public.kern1.O": ["O", "D"]} + expected = {"public.kern1.A": ("A", "Aacute"), "public.kern1.O": ("O", "D")} self.assertEqual(groups._get_side1KerningGroups(), expected) def test_side2KerningGroups(self): @@ -173,7 +173,7 @@ def test_side2KerningGroups(self): def test_get_side2KerningGroups(self): groups = self.getGroups_kerning() - expected = {"public.kern1.A": ["A", "Aacute"], "public.kern1.O": ["O", "D"]} + expected = {"public.kern1.A": ("A", "Aacute"), "public.kern1.O": ("O", "D")} self.assertEqual(groups._get_side1KerningGroups(), expected) # ----