diff --git a/Lib/fontParts/base/segment.py b/Lib/fontParts/base/segment.py index fb07a187..c62a4e80 100644 --- a/Lib/fontParts/base/segment.py +++ b/Lib/fontParts/base/segment.py @@ -1,5 +1,5 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Generator, List, Optional, Tuple, Union +from typing import TYPE_CHECKING, Any, Generator, List, Optional, Tuple from fontParts.base.errors import FontPartsError from fontParts.base.base import ( @@ -51,7 +51,7 @@ def _reprContents(self) -> List[str]: # this class should not be used in hashable # collections since it is dynamically generated. - __hash__ = None + __hash__ = None # type: ignore[assignment] # ------- # Parents @@ -62,7 +62,21 @@ def _reprContents(self) -> List[str]: _contour: Optional[BaseContour] = None contour: dynamicProperty = dynamicProperty( - "contour", "The segment's parent contour." + "contour", + """Get or set the segment's parent contour object. + + The value must be a :class:`BaseContour` instance or :obj:`None`. + + :return: The :class:`BaseContour` instance containing the segment + or :obj:`None`. + :raises AssertionError: If attempting to set the contour when it + has already been set. + + Example:: + + >>> contour = segment.contour + + """, ) def _get_contour(self) -> Optional[BaseContour]: @@ -79,7 +93,23 @@ def _set_contour(self, contour: Optional[BaseContour]) -> None: # Glyph - glyph: dynamicProperty = dynamicProperty("glyph", "The segment's parent glyph.") + glyph: dynamicProperty = dynamicProperty( + "glyph", + """Get the segment's parent glyph object. + + This property is read-only. + + The value must be a :class:`BaseGlyph` instance or :obj:`None`. + + :return: The :class:`BaseGlyph` instance containing the segment + or :obj:`None`. + + Example:: + + >>> glyph = segment.glyph + + """, + ) def _get_glyph(self) -> Optional[BaseGlyph]: if self._contour is None: @@ -88,7 +118,21 @@ def _get_glyph(self) -> Optional[BaseGlyph]: # Layer - layer: dynamicProperty = dynamicProperty("layer", "The segment's parent layer.") + layer: dynamicProperty = dynamicProperty( + "layer", + """Get the segment's parent layer object. + + This property is read-only. + + :return: The :class:`BaseLayer` instance containing the segemnt + or :obj:`None`. + + Example:: + + >>> layer = segment.layer + + """, + ) def _get_layer(self) -> Optional[BaseLayer]: if self._contour is None: @@ -97,7 +141,21 @@ def _get_layer(self) -> Optional[BaseLayer]: # Font - font: dynamicProperty = dynamicProperty("font", "The segment's parent font.") + font: dynamicProperty = dynamicProperty( + "font", + """Get the segment's parent font object. + + This property is read-only. + + :return: The :class:`BaseFont` instance containing the segment + or :obj:`None`. + + Example:: + + >>> font = segment.font + + """, + ) def _get_font(self) -> Optional[BaseFont]: if self._contour is None: @@ -109,15 +167,22 @@ def _get_font(self) -> Optional[BaseFont]: # -------- def __eq__(self, other: object) -> bool: - """ + """Check for equality with another segment. + The :meth:`BaseObject.__eq__` method can't be used here because the :class:`BaseContour` implementation contructs - segment objects without assigning an underlying ``naked`` + segment objects without assigning an underlying `naked` object. Therefore, comparisons will always fail. This method overrides the base method and compares the :class:`BasePoint` contained by the segment. - Subclasses may override this method. + :param other: The segment to compare with. + :return: :obj:`True` if the segments are equal, :obj:`False` otherwise. + + .. note:: + + Subclasses may override this method. + """ if isinstance(other, self.__class__): return self.points == other.points @@ -129,10 +194,20 @@ def __eq__(self, other: object) -> bool: index: dynamicProperty = dynamicProperty( "base_index", - ( - "The index of the segment within the ordered " - "list of the parent contour's segments." - ), + """Get the index of the segment. + + This property is read-only. + + :return: An :class:`int` representing the segment's index within an + ordered list of the parent contour's segments, or :obj:`None` if the + segment does not belong to a contour. + + Example:: + + >>> segment.index + 0 + + """, ) def _get_base_index(self) -> Optional[int]: @@ -143,8 +218,19 @@ def _get_base_index(self) -> Optional[int]: return normalizedValue def _get_index(self) -> int: - """ - Subclasses may override this method. + """Get the index of the native segment. + + This is the environment implementation of the :attr:`BaseSegment.index` + property getter. + + :return: An :class:`int` representing the segment's index within an + ordered list of the parent contour's segments, or :obj:`None` if the + segment does not belong to a contour. + + .. note:: + + Subclasses may override this method. + """ contour = self.contour value = contour.segments.index(self) @@ -156,7 +242,23 @@ def _get_index(self) -> int: type: dynamicProperty = dynamicProperty( "base_type", - ("The segment type. The possible types are " "move, line, curve, qcurve."), + """Get or set the segment's type. + + The value must be a :class:`str` containing one of the following + alternatives: + + +----------------+---------------------------------+ + | Type | Description | + +----------------+---------------------------------+ + | ``'move'`` | An on-curve move to. | + | ``'line'`` | An on-curve line to. | + | ``'curve'`` | An on-curve cubic curve to. | + | ``'qcurve'`` | An on-curve quadratic curve to. | + +----------------+---------------------------------+ + + :return: A :class:`str` representing the type of the segment. + + """, ) def _get_base_type(self) -> str: @@ -169,8 +271,18 @@ def _set_base_type(self, value: str) -> None: self._set_type(value) def _get_type(self) -> str: - """ - Subclasses may override this method. + """Get the native segment's type. + + This is the environment implementation of the :attr:`BaseSegment.type` + property getter. + + :return: A :class:`str` representing the type of the segment. The value + will have been normalized with :func:`normalizers.normalizeSegmentType`. + + .. note:: + + Subclasses may override this method. + """ onCurve = self.onCurve if onCurve is None: @@ -178,8 +290,19 @@ def _get_type(self) -> str: return onCurve.type def _set_type(self, newType: str) -> None: - """ - Subclasses may override this method. + """Set the native segment's type. + + This is the environment implementation of the :attr:`BaseSegment.type` + property setter. + + :param newType: The segment type definition as a :class:`str`. The value + will have been normalized with :func:`normalizers.normalizeSegmentType`. + :raises FontPartsError: If the segment does not belong to a contour. + + .. note:: + + Subclasses may override this method. + """ oldType = self.type if oldType == newType: @@ -218,7 +341,20 @@ def _set_type(self, newType: str) -> None: self.onCurve.type = newType smooth: dynamicProperty = dynamicProperty( - "base_smooth", ("Boolean indicating if the segment is " "smooth or not.") + "base_smooth", + """Get or set the segment's smooth state. + + The value must be a :class:`bool` indicating the segment's smooth state. + + :return: :obj:`True` if the segment is smooth, :obj:`False` if it is sharp. + + Example:: + + >>> segment.smooth + False + >>> segment.smooth = True + + """, ) def _get_base_smooth(self) -> bool: @@ -231,8 +367,19 @@ def _set_base_smooth(self, value: bool) -> None: self._set_smooth(value) def _get_smooth(self) -> bool: - """ - Subclasses may override this method. + """Get the native segment's smooth state. + + This is the environment implementation of the :attr:`BaseSegment.smooth` + property getter. + + :return: :obj:`True` if the segment is smooth, :obj:`False` if it is + sharp. The value will have been normalized + with :func:`normalizers.normalizeBoolean`. + + .. note:: + + Subclasses may override this method. + """ onCurve = self.onCurve if onCurve is None: @@ -240,8 +387,18 @@ def _get_smooth(self) -> bool: return onCurve.smooth def _set_smooth(self, value: bool) -> None: - """ - Subclasses may override this method. + """Set the native segment's smooth state. + + This is the environment implementation of the :attr:`BaseSegment.smooth` + property setter. + + :param value: The point's smooth state as a :class:`bool`. The value + will have been normalized with :func:`normalizers.normalizeBoolean`. + + .. note:: + + Subclasses may override this method. + """ onCurve = self.onCurve if onCurve is not None: @@ -252,20 +409,54 @@ def _set_smooth(self, value: bool) -> None: # ------ def __getitem__(self, index: int) -> BasePoint: + """Get the point at the specified index. + + :param index: The zero-based index of the point to retrieve as + an :class:`int`. + :return: The :class:`BasePoint` instance located at the specified `index`. + :raises IndexError: If the specified `index` is out of range. + + """ return self._getItem(index) def _getItem(self, index: int) -> BasePoint: - """ - Subclasses may override this method. + """Get the native point at the specified index. + + This is the environment implementation of :meth:`BaseSegment.__getitem__`. + + :param index: The zero-based index of the point to retrieve as + an :class:`int`. + :return: The :class:`BasePoint` instance located at the specified `index`. + :raises IndexError: If the specified `index` is out of range. + + .. note:: + + Subclasses may override this method. + """ return self.points[index] def __iter__(self): + """Return an iterator over the points in the segment. + + :return: An iterator over the :class:`BasePoint` instances belonging to + the segment. + + """ return self._iterPoints() def _iterPoints(self, **kwargs: Any) -> Generator[BasePoint]: - """ - Subclasses may override this method. + """Return an iterator over the points in the native segment. + + This is the environment implementation of :meth:`BaseSegment.__iter__`. + + :return: An iterator over the :class:`BasePoint` instances belonging to + the segment. + + .. note:: + + Subclasses may override this method. + """ points = self.points count = len(points) @@ -276,39 +467,86 @@ def _iterPoints(self, **kwargs: Any) -> Generator[BasePoint]: index += 1 def __len__(self) -> int: + """Return the number of points in the segment. + + :return: An :class:`int` representing the number of :class:`BasePoint` + instances belonging to the segment. + + """ return self._len() def _len(self, **kwargs: Any) -> int: - """ - Subclasses may override this method. + """Return the number of points in the native segment. + + This is the environment implementation of :meth:`BaseSegment.__len__`. + + :return: An :class:`int` representing the number of :class:`BasePoint` + instances belonging to the segment. + + .. note:: + + Subclasses may override this method. + """ return len(self.points) points: dynamicProperty = dynamicProperty( - "base_points", "A list of points in the segment." + "base_points", + """Get a list of all points in the segment. + + This attribute is read-only. + + :return: A :class:`tuple` of :class`BasePoints`. + + """, ) def _get_base_points(self) -> Tuple[BasePoint, ...]: return self._get_points() def _get_points(self) -> Tuple[BasePoint, ...]: - """ - Subclasses may override this method. + """Get a list of all points in the native segment. + + This is the environment implementation of the :attr:`BaseSegment.points` + property getter. + + :return: A :class:`tuple` of :class`BasePoints`. + + .. note:: + + Subclasses may override this method. + """ if not hasattr(self, "_points"): return () return tuple(self._points) onCurve: dynamicProperty = dynamicProperty( - "base_onCurve", "The on curve point in the segment." + "base_onCurve", + """Get the on-curve point in the segment. + + This property is read-only. + + :return: An on-curve :class:`BasePoint` instance or :obj:`None`. + + """, ) def _get_base_onCurve(self) -> Optional[BasePoint]: return self._get_onCurve() def _get_onCurve(self) -> Optional[BasePoint]: - """ - Subclasses may override this method. + """Get the on-curve point in the native segment. + + This is the environment implementation of + the :attr:`BaseSegment.onCurve` property getter. + + :return: An on-curve :class:`BasePoint` instance or :obj:`None`. + + .. note:: + + Subclasses may override this method. + """ value = self.points[-1] if value.type == "offcurve": @@ -316,18 +554,31 @@ def _get_onCurve(self) -> Optional[BasePoint]: return value offCurve: dynamicProperty = dynamicProperty( - "base_offCurve", "The off curve points in the segment." + "base_offCurve", + """Get the off-curve points in the segment. + + This property is read-only. + + :return: An off-curve :class:`BasePoint` instance or :obj:`None`. + + """, ) def _get_base_offCurve(self) -> Tuple[BasePoint, ...]: - """ - Subclasses may override this method. - """ return self._get_offCurve() def _get_offCurve(self) -> Tuple[BasePoint, ...]: - """ - Subclasses may override this method. + """Get the off-curve points in the native segment. + + This is the environment implementation of + the :attr:`BaseSegment.offCurve` property getter. + + :return: An off-curve :class:`BasePoint` instance or :obj:`None`. + + .. note:: + + Subclasses may override this method. + """ if self.points and self.points[-1].type == "offcurve": return self.points @@ -338,8 +589,18 @@ def _get_offCurve(self) -> Tuple[BasePoint, ...]: # -------------- def _transformBy(self, matrix: SextupleCollectionType[IntFloatType], **kwargs: Any): - """ - Subclasses may override this method. + r"""Transform the native object according to the given matrix. + + This is the environment implementation of :meth:`TransformationMixin.transformBy`. + + :param matrix: The :ref:`type-transformation` to apply. The value will have + been normalized with :func:`normalizers.normalizeTransformationMatrix`. + :param \**kwargs: Additional keyword arguments. + + .. note:: + + Subclasses may override this method. + """ for point in self.points: point.transformBy(matrix) @@ -351,8 +612,19 @@ def _transformBy(self, matrix: SextupleCollectionType[IntFloatType], **kwargs: A compatibilityReporterClass = SegmentCompatibilityReporter def isCompatible(self, other: BaseSegment) -> Tuple[bool, str]: - """ - Evaluate interpolation compatibility with **other**. :: + """Evaluate interpolation compatibility with another segment. + + This method will return a :class:`bool` indicating if the segment is + compatible for interpolation with `other`, and a :class:`str` + containing compatibility notes. + + :param other: The other :class:`BaseSegment` instance to check + compatibility with. + :return: A :class:`tuple` where the first element is a :class:`bool` + indicating compatibility, and the second element is a :class:`str` + of compatibility notes. + + Example:: >>> compatible, report = self.isCompatible(otherSegment) >>> compatible @@ -363,20 +635,24 @@ def isCompatible(self, other: BaseSegment) -> Tuple[bool, str]: [Fatal] Segment: [1] + [1] [Fatal] Segment: [1] is line | [1] is qcurve - This will return a ``bool`` indicating if the segment is - compatible for interpolation with **other** and a - :ref:`type-string` of compatibility notes. """ return super(BaseSegment, self).isCompatible(other, BaseSegment) def _isCompatible( self, other: BaseSegment, reporter: SegmentCompatibilityReporter ) -> None: - """ - This is the environment implementation of - :meth:`BaseSegment.isCompatible`. + """Evaluate interpolation compatibility with another native segment. + + This is the environment implementation of :meth:`BaseSegment.isCompatible`. + + :param other: The other :class:`BaseSegment` instance to check + compatibility with. + :param reporter: An object used to report compatibility issues. + + .. note:: + + Subclasses may override this method. - Subclasses may override this method. """ segment1 = self segment2 = other @@ -392,8 +668,6 @@ def _isCompatible( # ---- def round(self) -> None: - """ - Round coordinates in all points. - """ + """Round coordinates in all the segment's points.""" for point in self.points: point.round()