From 93b5016a298744c17ae6ce02888b261a89227b75 Mon Sep 17 00:00:00 2001 From: David Salvisberg Date: Wed, 29 May 2024 14:38:19 +0200 Subject: [PATCH] reportlab: Add some basic type hints for graphics rendering API (#12051) --- stubs/reportlab/@tests/stubtest_allowlist.txt | 1 + .../@tests/test_cases/check_tables.py | 7 + .../reportlab/graphics/renderPDF.pyi | 10 +- .../reportlab/reportlab/graphics/renderPM.pyi | 40 +- .../reportlab/reportlab/graphics/renderPS.pyi | 10 +- .../reportlab/graphics/renderSVG.pyi | 10 +- stubs/reportlab/reportlab/graphics/shapes.pyi | 438 +++++++++++------- .../reportlab/graphics/widgetbase.pyi | 5 +- .../reportlab/graphics/widgets/grids.pyi | 4 +- stubs/reportlab/reportlab/platypus/tables.pyi | 2 +- 10 files changed, 332 insertions(+), 195 deletions(-) diff --git a/stubs/reportlab/@tests/stubtest_allowlist.txt b/stubs/reportlab/@tests/stubtest_allowlist.txt index 4e5160000..4e31860ee 100644 --- a/stubs/reportlab/@tests/stubtest_allowlist.txt +++ b/stubs/reportlab/@tests/stubtest_allowlist.txt @@ -8,6 +8,7 @@ reportlab\.platypus\.(doctemplate\.|flowables\.)?[A-Za-z_]+\.drawOn # similary the wrap/split methods use inconsistent names for their # parameters, so we decided to make them positional-only +reportlab.graphics.shapes.Drawing.wrap reportlab\.platypus\.(doctemplate\.|flowables\.|tableofcontents\.)?[A-Za-z_]+\.split reportlab\.platypus\.(doctemplate\.|flowables\.|tableofcontents\.)?[A-Za-z_]+\.wrap reportlab.platypus.multicol.MultiCol.split diff --git a/stubs/reportlab/@tests/test_cases/check_tables.py b/stubs/reportlab/@tests/test_cases/check_tables.py index e7fa0bd31..0d1b93de8 100644 --- a/stubs/reportlab/@tests/test_cases/check_tables.py +++ b/stubs/reportlab/@tests/test_cases/check_tables.py @@ -190,3 +190,10 @@ Table( ("ROUNDEDCORNERS", [0, 0, 5, 5]), ], ) + + +# Testing the various possible data layouts +Table([["foo"]]) +Table([("foo",)]) +Table((["foo"],)) +Table((("foo",),)) diff --git a/stubs/reportlab/reportlab/graphics/renderPDF.pyi b/stubs/reportlab/reportlab/graphics/renderPDF.pyi index 37486407b..f8076eebd 100644 --- a/stubs/reportlab/reportlab/graphics/renderPDF.pyi +++ b/stubs/reportlab/reportlab/graphics/renderPDF.pyi @@ -1,12 +1,14 @@ from _typeshed import Incomplete -from typing import Final +from typing import IO, Final from reportlab.graphics.renderbase import Renderer +from reportlab.graphics.shapes import Drawing +from reportlab.pdfgen.canvas import Canvas from reportlab.platypus import Flowable __version__: Final[str] -def draw(drawing, canvas, x, y, showBoundary=...) -> None: ... +def draw(drawing: Drawing, canvas: Canvas, x: float, y: float, showBoundary=...) -> None: ... class _PDFRenderer(Renderer): def __init__(self) -> None: ... @@ -32,6 +34,6 @@ class GraphicsFlowable(Flowable): def __init__(self, drawing) -> None: ... def draw(self) -> None: ... -def drawToFile(d, fn, msg: str = "", showBoundary=..., autoSize: int = 1, canvasKwds={}) -> None: ... -def drawToString(d, msg: str = "", showBoundary=..., autoSize: int = 1, canvasKwds={}): ... +def drawToFile(d: Drawing, fn: str | IO[bytes], msg: str = "", showBoundary=..., autoSize: int = 1, canvasKwds={}) -> None: ... +def drawToString(d: Drawing, msg: str = "", showBoundary=..., autoSize: int = 1, canvasKwds={}) -> str: ... def test(outDir: str = "pdfout", shout: bool = False) -> None: ... diff --git a/stubs/reportlab/reportlab/graphics/renderPM.pyi b/stubs/reportlab/reportlab/graphics/renderPM.pyi index 67c883e47..94d881b74 100644 --- a/stubs/reportlab/reportlab/graphics/renderPM.pyi +++ b/stubs/reportlab/reportlab/graphics/renderPM.pyi @@ -1,13 +1,15 @@ from _typeshed import Incomplete -from typing import Final +from typing import IO, Final from reportlab.graphics.renderbase import Renderer +from reportlab.graphics.shapes import Drawing +from reportlab.pdfgen.canvas import Canvas __version__: Final[str] def Color2Hex(c): ... def CairoColor(c): ... -def draw(drawing, canvas, x, y, showBoundary=...) -> None: ... +def draw(drawing: Drawing, canvas: Canvas, x: float, y: float, showBoundary=...) -> None: ... class _PMRenderer(Renderer): def pop(self) -> None: ... @@ -72,53 +74,53 @@ class PMCanvas: def setLineWidth(self, width) -> None: ... def drawToPMCanvas( - d, - dpi: int = 72, - bg: int = 16777215, + d: Drawing, + dpi: float = 72, + bg: int = 0xFFFFFF, configPIL: Incomplete | None = None, showBoundary=..., backend="rlPyCairo", backendFmt: str = "RGB", ): ... def drawToPIL( - d, - dpi: int = 72, - bg: int = 16777215, + d: Drawing, + dpi: float = 72, + bg: int = 0xFFFFFF, configPIL: Incomplete | None = None, showBoundary=..., backend="rlPyCairo", backendFmt: str = "RGB", ): ... def drawToPILP( - d, - dpi: int = 72, - bg: int = 16777215, + d: Drawing, + dpi: float = 72, + bg: int = 0xFFFFFF, configPIL: Incomplete | None = None, showBoundary=..., backend="rlPyCairo", backendFmt: str = "RGB", ): ... def drawToFile( - d, - fn, + d: Drawing, + fn: str | IO[bytes], fmt: str = "GIF", - dpi: int = 72, - bg: int = 16777215, + dpi: float = 72, + bg: int = 0xFFFFFF, configPIL: Incomplete | None = None, showBoundary=..., backend="rlPyCairo", backendFmt: str = "RGB", ) -> None: ... def drawToString( - d, + d: Drawing, fmt: str = "GIF", - dpi: int = 72, - bg: int = 16777215, + dpi: float = 72, + bg: int = 0xFFFFFF, configPIL: Incomplete | None = None, showBoundary=..., backend="rlPyCairo", backendFmt: str = "RGB", -): ... +) -> str: ... save = drawToFile diff --git a/stubs/reportlab/reportlab/graphics/renderPS.pyi b/stubs/reportlab/reportlab/graphics/renderPS.pyi index 900502a8a..105d78791 100644 --- a/stubs/reportlab/reportlab/graphics/renderPS.pyi +++ b/stubs/reportlab/reportlab/graphics/renderPS.pyi @@ -1,7 +1,9 @@ from _typeshed import Incomplete -from typing import Final +from typing import IO, Final from reportlab.graphics.renderbase import Renderer +from reportlab.graphics.shapes import Drawing +from reportlab.pdfgen.canvas import Canvas __version__: Final[str] PS_WinAnsiEncoding: Final[str] @@ -50,7 +52,7 @@ class PSCanvas: def scale(self, x, y) -> None: ... def transform(self, a, b, c, d, e, f) -> None: ... -def draw(drawing, canvas, x: int = 0, y: int = 0, showBoundary=0) -> None: ... +def draw(drawing: Drawing, canvas: Canvas, x: float = 0, y: float = 0, showBoundary=0) -> None: ... class _PSRenderer(Renderer): def drawNode(self, node) -> None: ... @@ -66,6 +68,6 @@ class _PSRenderer(Renderer): def applyStateChanges(self, delta, newState) -> None: ... def drawImage(self, image) -> None: ... -def drawToFile(d, fn, showBoundary=0, **kwd) -> None: ... -def drawToString(d, showBoundary=0): ... +def drawToFile(d: Drawing, fn: IO[bytes], showBoundary=0, **kwd) -> None: ... +def drawToString(d: Drawing, showBoundary=0) -> str: ... def test(outDir: str = "epsout", shout: bool = False) -> None: ... diff --git a/stubs/reportlab/reportlab/graphics/renderSVG.pyi b/stubs/reportlab/reportlab/graphics/renderSVG.pyi index fdd933ae4..b687ee9a8 100644 --- a/stubs/reportlab/reportlab/graphics/renderSVG.pyi +++ b/stubs/reportlab/reportlab/graphics/renderSVG.pyi @@ -1,9 +1,11 @@ from _typeshed import Incomplete from collections.abc import Sequence from math import cos as cos, pi as pi, sin as sin -from typing import Final +from typing import IO, Final from reportlab.graphics.renderbase import Renderer +from reportlab.graphics.shapes import Drawing +from reportlab.pdfgen.canvas import Canvas AREA_STYLES: Final[Sequence[str]] LINE_STYLES: Final[Sequence[str]] @@ -11,9 +13,9 @@ TEXT_STYLES: Final[Sequence[str]] EXTRA_STROKE_STYLES: Final[Sequence[str]] EXTRA_FILL_STYLES: Final[Sequence[str]] -def drawToString(d, showBoundary=0, **kwds): ... -def drawToFile(d, fn, showBoundary=0, **kwds) -> None: ... -def draw(drawing, canvas, x: int = 0, y: int = 0, showBoundary=0) -> None: ... +def drawToString(d: Drawing, showBoundary=0, **kwds) -> str: ... +def drawToFile(d: Drawing, fn: str | IO[str], showBoundary=0, **kwds) -> None: ... +def draw(drawing: Drawing, canvas: Canvas, x: float = 0, y: float = 0, showBoundary=0) -> None: ... def transformNode(doc, newTag, node: Incomplete | None = None, **attrDict): ... class EncodedWriter(list[Incomplete]): diff --git a/stubs/reportlab/reportlab/graphics/shapes.pyi b/stubs/reportlab/reportlab/graphics/shapes.pyi index e872a62f3..886ca06f3 100644 --- a/stubs/reportlab/reportlab/graphics/shapes.pyi +++ b/stubs/reportlab/reportlab/graphics/shapes.pyi @@ -1,11 +1,96 @@ -from _typeshed import Incomplete -from typing import Final +from _typeshed import Incomplete, SupportsItems +from abc import abstractmethod +from collections.abc import Iterable, Sequence +from typing import Any, Final, Literal, NoReturn, TypedDict +from typing_extensions import Self, TypeAlias, Unpack -from reportlab.lib.attrmap import * -from reportlab.lib.validators import * +from reportlab.lib.colors import Color +from reportlab.lib.validators import NoneOr, Validator +from reportlab.pdfgen.canvas import Canvas from reportlab.platypus import Flowable +from reportlab.platypus.flowables import _HAlignment, _VAlignment -from .transform import * +_IntBool: TypeAlias = Literal[0, 1] +_BoolLike: TypeAlias = _IntBool | bool +_PathOp: TypeAlias = ( + tuple[Literal["moveTo"], float, float] + | tuple[Literal["lineTo"], float, float] + | tuple[Literal["curveTo"], float, float, float, float, float, float] + # close path may either be a tuple or just the string + | Literal["closePath"] + | tuple[Literal["closePath"]] + # fallback for list that is not type safe + | list[Any] +) + +# NOTE: These are derived from _attrMap and can optionally be +# verified at runtime +class _GroupKwArgs(TypedDict, total=False): + transform: tuple[float, float, float, float, float, float] | list[float] | list[int] + # NOTE: This should be used with care, since it will replace elements + # it's mostly useful for circumventing validation logic and + # reusing the list, rather than populating a new list + contents: list[Shape] + strokeOverprint: _BoolLike + fillOverprint: _BoolLike + overprintMask: _BoolLike + +class _DrawingKwArgs(_GroupKwArgs, total=False): + # TODO: Restrict to supported formats? + formats: list[str] | tuple[str, ...] + # NOTE: This looks like an implementation detail, so we may not + # want to include this in KwArgs + canv: Canvas + background: Shape | UserNode | None + # NOTE: The runtime validation for alignments is incorrect, so + # we assume it is turned off and allow all valid values + hAlign: _HAlignment + vAlign: _VAlignment + renderScale: float + initialFontName: str | None + initialFontSize: float | None + +class _LineShapeKwArgs(TypedDict, total=False): + strokeColor: Color | None + strokeWidth: float + strokeLineCap: Literal[0, 1, 2] + strokeLineJoin: Literal[0, 1, 2] + strokeMiterLimit: float + strokeDashArray: Sequence[float] | tuple[float, Sequence[float]] + strokeOpacity: float | None + strokeOverprint: _BoolLike + overprintMask: _BoolLike + +class _PathKwArgs(_LineShapeKwArgs, total=False): + fillColor: Color | None + fillOpacity: float + fillOverprint: _BoolLike + +class _AllPathKwArgs(_PathKwArgs, total=False): + points: list[float] | None + operators: list[float] | None + isClipPath: _BoolLike + autoclose: Literal["svg", "pdf"] | None + fillMode: Literal[0, 1] + +class _SolidShapeKwArgs(_PathKwArgs, total=False): + fillMode: Literal[0, 1] + +class _DefinePathKwArgs(_SolidShapeKwArgs, total=False): + autoclose: Literal["svg", "pdf"] | None + bbox: tuple[float, float, float, float] | None + +class _WedgeKwArgs(_SolidShapeKwArgs, total=False): + radius1: float | None + yradius1: float | None + +class _StringKwArgs(TypedDict, total=False): + fontName: str + fontSize: float + fillColor: Color | None + textAnchor: Literal["start", "middle", "end", "numeric"] + encoding: str + textRenderMode: Literal[0, 1, 2, 3, 4, 5, 6, 7] __version__: Final[str] isOpacity: NoneOr @@ -16,61 +101,63 @@ STATE_DEFAULTS: Final[Incomplete] class _DrawTimeResizeable: ... class _SetKeyWordArgs: - def __init__(self, keywords={}) -> None: ... + def __init__(self, keywords: SupportsItems[str, Any] = {}) -> None: ... def getRectsBounds(rectList): ... def getPathBounds(points): ... def getPointsBounds(pointList): ... class Shape(_SetKeyWordArgs, _DrawTimeResizeable): - def copy(self) -> None: ... - def getProperties(self, recur: int = 1): ... + @abstractmethod + def copy(self) -> Self: ... + def getProperties(self, recur: int = 1) -> dict[str, Any]: ... def setProperties(self, props) -> None: ... def dumpProperties(self, prefix: str = "") -> None: ... def verify(self) -> None: ... - def __setattr__(self, attr, value) -> None: ... - def getBounds(self) -> None: ... + @abstractmethod + def getBounds(self) -> tuple[float, float, float, float]: ... class Group(Shape): - contents: Incomplete - transform: Incomplete - def __init__(self, *elements, **keywords) -> None: ... - def add(self, node, name: Incomplete | None = None) -> None: ... - def insert(self, i, n, name: Incomplete | None = None) -> None: ... - def expandUserNodes(self): ... - def copy(self): ... - def rotate(self, theta) -> None: ... - def translate(self, dx, dy) -> None: ... - def scale(self, sx, sy) -> None: ... - def skew(self, kx, ky) -> None: ... - def shift(self, x, y) -> None: ... - __class__: Incomplete - width: Incomplete - height: Incomplete - def asDrawing(self, width, height) -> None: ... - def getContents(self): ... - def getBounds(self): ... + contents: list[Shape] + transform: tuple[float, float, float, float, float, float] | list[float] | list[int] + def __init__(self, *elements: Shape | UserNode, **keywords: Unpack[_GroupKwArgs]) -> None: ... + def add(self, node: Shape | UserNode, name: str | None = None) -> None: ... + def insert(self, i: int, n: Shape | UserNode, name: str | None = None) -> None: ... + def expandUserNodes(self) -> Group: ... + def copy(self) -> Self: ... + def rotate(self, theta: float) -> None: ... + def translate(self, dx: float, dy: float) -> None: ... + def scale(self, sx: float, sy: float) -> None: ... + def skew(self, kx: float, ky: float) -> None: ... + def shift(self, x: float, y: float) -> None: ... + # NOTE: This changes the object to a Drawing, rather than returning + # a new one, which is not ideal... + def asDrawing(self, width: float, height: float) -> None: ... + def getContents(self) -> list[Shape | UserNode]: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class Drawing(Group, Flowable): - background: Incomplete + background: Shape | UserNode | None renderScale: float - def __init__(self, width: int = 400, height: int = 200, *nodes, **keywords) -> None: ... + def __init__( + self, width: float = 400, height: float = 200, *nodes: Shape | UserNode, **keywords: Unpack[_DrawingKwArgs] + ) -> None: ... def draw(self, showBoundary=...) -> None: ... - def wrap(self, availWidth, availHeight): ... - def expandUserNodes(self): ... - def copy(self): ... - def asGroup(self, *args, **kw): ... + def expandUserNodes(self) -> Drawing: ... + def asGroup(self, *args: Shape | UserNode, **kw: Unpack[_GroupKwArgs]) -> Group: ... def save( self, - formats: Incomplete | None = None, - verbose: Incomplete | None = None, - fnRoot: Incomplete | None = None, - outDir: Incomplete | None = None, + formats: Iterable[str] | None = None, + verbose: bool | None = None, + fnRoot: str | None = None, + outDir: str | None = None, title: str = "", **kw, ): ... - def asString(self, format, verbose: Incomplete | None = None, preview: int = 0, **kw): ... - def resized(self, kind: str = "fit", lpad: int = 0, rpad: int = 0, bpad: int = 0, tpad: int = 0): ... + def asString(self, format: str, verbose: bool | None = None, preview: int = 0, **kw) -> str: ... + def resized( + self, kind: Literal["fit", "fitx", "fity"] = "fit", lpad: float = 0, rpad: float = 0, bpad: float = 0, tpad: float = 0 + ) -> Drawing: ... class _DrawingEditorMixin: ... @@ -80,174 +167,205 @@ class _isStrokeDashArray(Validator): isStrokeDashArray: _isStrokeDashArray class LineShape(Shape): - strokeColor: Incomplete - strokeWidth: int - strokeLineCap: int - strokeLineJoin: int - strokeMiterLimit: int - strokeDashArray: Incomplete - strokeOpacity: Incomplete - def __init__(self, kw) -> None: ... + strokeColor: Color | None + strokeWidth: float + strokeLineCap: Literal[0, 1, 2] + strokeLineJoin: Literal[0, 1, 2] + strokeMiterLimit: float + strokeDashArray: Sequence[float] | tuple[float, Sequence[float]] + strokeOpacity: float | None + def __init__(self, kw: _LineShapeKwArgs) -> None: ... + @abstractmethod + def copy(self) -> Self: ... + @abstractmethod + def getBounds(self) -> tuple[float, float, float, float]: ... class Line(LineShape): - x1: Incomplete - y1: Incomplete - x2: Incomplete - y2: Incomplete - def __init__(self, x1, y1, x2, y2, **kw) -> None: ... - def getBounds(self): ... + x1: float + y1: float + x2: float + y2: float + def __init__(self, x1: float, y1: float, x2: float, y2: float, **kw: Unpack[_LineShapeKwArgs]) -> None: ... + # NOTE: For some reason Line doesn't implement copy + def copy(self) -> NoReturn: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class SolidShape(LineShape): - fillColor: Incomplete - fillOpacity: Incomplete - def __init__(self, kw) -> None: ... + fillColor: Color | None + fillOpacity: float | None + def __init__(self, kw: _SolidShapeKwArgs) -> None: ... + @abstractmethod + def copy(self) -> Self: ... + @abstractmethod + def getBounds(self) -> tuple[float, float, float, float]: ... class Path(SolidShape): - points: Incomplete - operators: Incomplete - isClipPath: Incomplete - autoclose: Incomplete - fillMode: Incomplete + points: list[float] + operators: list[float] + isClipPath: _BoolLike + autoclose: Literal["svg", "pdf"] | None + fillMode: Literal[0, 1] def __init__( self, - points: Incomplete | None = None, - operators: Incomplete | None = None, - isClipPath: int = 0, - autoclose: Incomplete | None = None, - fillMode=0, - **kw, + points: list[float] | None = None, + operators: list[float] | None = None, + isClipPath: _BoolLike = 0, + autoclose: Literal["svg", "pdf"] | None = None, + fillMode: Literal[0, 1] = 0, + **kw: Unpack[_PathKwArgs], ) -> None: ... - def copy(self): ... - def moveTo(self, x, y) -> None: ... - def lineTo(self, x, y) -> None: ... - def curveTo(self, x1, y1, x2, y2, x3, y3) -> None: ... + def copy(self) -> Self: ... + def moveTo(self, x: float, y: float) -> None: ... + def lineTo(self, x: float, y: float) -> None: ... + def curveTo(self, x1: float, y1: float, x2: float, y2: float, x3: float, y3: float) -> None: ... def closePath(self) -> None: ... - def getBounds(self): ... + def getBounds(self) -> tuple[float, float, float, float]: ... -EmptyClipPath: Incomplete +EmptyClipPath: Final[Path] def getArcPoints( - centerx, - centery, - radius, - startangledegrees, - endangledegrees, - yradius: Incomplete | None = None, - degreedelta: Incomplete | None = None, - reverse: Incomplete | None = None, -): ... + centerx: float, + centery: float, + radius: float, + startangledegrees: float, + endangledegrees: float, + yradius: float | None = None, + degreedelta: float | None = None, + reverse: _BoolLike | None = None, +) -> list[float]: ... class ArcPath(Path): def addArc( self, - centerx, - centery, - radius, - startangledegrees, - endangledegrees, - yradius: Incomplete | None = None, - degreedelta: Incomplete | None = None, - moveTo: Incomplete | None = None, - reverse: Incomplete | None = None, + centerx: float, + centery: float, + radius: float, + startangledegrees: float, + endangledegrees: float, + yradius: float | None = None, + degreedelta: float | None = None, + moveTo: _BoolLike | None = None, + reverse: _BoolLike | None = None, ) -> None: ... -def definePath(pathSegs=[], isClipPath: int = 0, dx: int = 0, dy: int = 0, **kw): ... +def definePath( + pathSegs: Iterable[_PathOp] = [], isClipPath: _BoolLike = 0, dx: float = 0, dy: float = 0, **kw: Unpack[_DefinePathKwArgs] +) -> Path: ... class Rect(SolidShape): - x: Incomplete - y: Incomplete - width: Incomplete - height: Incomplete - rx: Incomplete - ry: Incomplete - def __init__(self, x, y, width, height, rx: int = 0, ry: int = 0, **kw) -> None: ... - def copy(self): ... - def getBounds(self): ... + x: float + y: float + width: float + height: float + rx: float + ry: float + def __init__( + self, x: float, y: float, width: float, height: float, rx: float = 0, ry: float = 0, **kw: _SolidShapeKwArgs + ) -> None: ... + def copy(self) -> Self: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class Image(SolidShape): - x: Incomplete - y: Incomplete - width: Incomplete - height: Incomplete + x: float + y: float + width: float + height: float path: Incomplete - def __init__(self, x, y, width, height, path, **kw) -> None: ... - def copy(self): ... - def getBounds(self): ... + def __init__(self, x: float, y: float, width: float, height: float, path, **kw: Unpack[_SolidShapeKwArgs]) -> None: ... + def copy(self) -> Self: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class Circle(SolidShape): - cx: Incomplete - cy: Incomplete - r: Incomplete - def __init__(self, cx, cy, r, **kw) -> None: ... - def copy(self): ... - def getBounds(self): ... + cx: float + cy: float + r: float + def __init__(self, cx: float, cy: float, r: float, **kw: Unpack[_SolidShapeKwArgs]) -> None: ... + def copy(self) -> Self: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class Ellipse(SolidShape): - cx: Incomplete - cy: Incomplete - rx: Incomplete - ry: Incomplete - def __init__(self, cx, cy, rx, ry, **kw) -> None: ... - def copy(self): ... - def getBounds(self): ... + cx: float + cy: float + rx: float + ry: float + def __init__(self, cx: float, cy: float, rx: float, ry: float, **kw: Unpack[_SolidShapeKwArgs]) -> None: ... + def copy(self) -> Self: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class Wedge(SolidShape): - degreedelta: int - yradius: Incomplete - annular: Incomplete + centerx: float + centery: float + radius: float + startangledegrees: float + endangledegrees: float + yradius: float | None + annular: bool + # NOTE: This one is not actually settable on the instance if runtime validation + # is turned on, but it seems bad to disallow it anyways + degreedelta: float def __init__( self, - centerx, - centery, - radius, - startangledegrees, - endangledegrees, - yradius: Incomplete | None = None, + centerx: float, + centery: float, + radius: float, + startangledegrees: float, + endangledegrees: float, + yradius: float | None = None, annular: bool = False, - **kw, + **kw: Unpack[_WedgeKwArgs], ) -> None: ... - def asPolygon(self): ... - def copy(self): ... - def getBounds(self): ... + def asPolygon(self) -> Path | Polygon: ... + def copy(self) -> Self: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class Polygon(SolidShape): - points: Incomplete - def __init__(self, points=[], **kw) -> None: ... - def copy(self): ... - def getBounds(self): ... + points: list[float] + def __init__(self, points: list[float] = [], **kw: Unpack[_SolidShapeKwArgs]) -> None: ... + def copy(self) -> Self: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class PolyLine(LineShape): - points: Incomplete - def __init__(self, points=[], **kw) -> None: ... - def copy(self): ... - def getBounds(self): ... + points: list[float] + def __init__(self, points: list[float] = [], **kw: Unpack[_SolidShapeKwArgs]) -> None: ... + def copy(self) -> Self: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class Hatching(Path): - xyLists: Incomplete - angles: Incomplete - spacings: Incomplete - def __init__(self, spacings: int = 2, angles: int = 45, xyLists=[], **kwds) -> None: ... + xyLists: Sequence[tuple[float, float]] + angles: Sequence[float] + spacings: Sequence[float] + def __init__( + self, + spacings: float | Sequence[float] = 2, + angles: float | Sequence[float] = 45, + xyLists: Sequence[tuple[float, float] | list[float]] = [], + **kwds: Unpack[_AllPathKwArgs], + ) -> None: ... -def numericXShift(tA, text, w, fontName, fontSize, encoding: Incomplete | None = None, pivotCharacter="."): ... +def numericXShift( + tA, text: str, w: float, fontName: str, fontSize: float, encoding: str | None = None, pivotCharacter: str = "." +) -> float: ... class String(Shape): encoding: str - x: Incomplete - y: Incomplete - text: Incomplete - textAnchor: str - fontName: Incomplete - fontSize: Incomplete - fillColor: Incomplete - def __init__(self, x, y, text, **kw) -> None: ... + x: float + y: float + text: str + textAnchor: Literal["start", "middle", "end", "numeric"] + fontName: str + fontSize: float + fillColor: Color | None + def __init__(self, x: float, y: float, text: str, **kw: Unpack[_StringKwArgs]) -> None: ... def getEast(self): ... - def copy(self): ... - def getBounds(self): ... + def copy(self) -> Self: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class UserNode(_DrawTimeResizeable): - def provideNode(self) -> None: ... + @abstractmethod + def provideNode(self) -> Shape: ... class DirectDraw(Shape): - def drawDirectly(self, canvas) -> None: ... + @abstractmethod + def drawDirectly(self, canvas: Canvas) -> None: ... def test() -> None: ... diff --git a/stubs/reportlab/reportlab/graphics/widgetbase.pyi b/stubs/reportlab/reportlab/graphics/widgetbase.pyi index a67d94116..9ecbd223f 100644 --- a/stubs/reportlab/reportlab/graphics/widgetbase.pyi +++ b/stubs/reportlab/reportlab/graphics/widgetbase.pyi @@ -15,10 +15,11 @@ class PropHolder: def dumpProperties(self, prefix: str = "") -> None: ... class Widget(PropHolder, shapes.UserNode): + # TODO: draw should probably be marked abstract def draw(self) -> None: ... def demo(self) -> None: ... - def provideNode(self): ... - def getBounds(self): ... + def provideNode(self) -> shapes.Shape: ... + def getBounds(self) -> tuple[float, float, float, float]: ... class ScaleWidget(Widget): x: Incomplete diff --git a/stubs/reportlab/reportlab/graphics/widgets/grids.pyi b/stubs/reportlab/reportlab/graphics/widgets/grids.pyi index fcf6d499f..1664fa63d 100644 --- a/stubs/reportlab/reportlab/graphics/widgets/grids.pyi +++ b/stubs/reportlab/reportlab/graphics/widgets/grids.pyi @@ -1,5 +1,5 @@ from _typeshed import Incomplete -from typing import Final +from typing import Final, NoReturn from reportlab.graphics.shapes import LineShape from reportlab.graphics.widgetbase import Widget @@ -72,3 +72,5 @@ class ShadedPolygon(Widget, LineShape): points: Incomplete def __init__(self, **kw) -> None: ... def draw(self): ... + # NOTE: widgets don't implement this, only actual shapes + def copy(self) -> NoReturn: ... diff --git a/stubs/reportlab/reportlab/platypus/tables.pyi b/stubs/reportlab/reportlab/platypus/tables.pyi index fefe2bb34..88893f074 100644 --- a/stubs/reportlab/reportlab/platypus/tables.pyi +++ b/stubs/reportlab/reportlab/platypus/tables.pyi @@ -63,9 +63,9 @@ class Table(Flowable): spaceAfter: float def __init__( self, - data: list[list[Any]] | tuple[tuple[Any, ...], ...], # NOTE: Technically only list or tuple works but lack of covariance # on list makes this too annoying + data: Sequence[list[Any] | tuple[Any, ...]], colWidths: Sequence[float | str | None] | float | str | None = None, rowHeights: Sequence[float | None] | float | None = None, style: TableStyle | Iterable[_TableCommand] | None = None,