From 83743593097596477b3c234918f62fd87ab86221 Mon Sep 17 00:00:00 2001 From: James Turk Date: Fri, 26 Apr 2024 16:31:12 -0500 Subject: [PATCH] mypy linting: everything except for WIP functional pattern --- .github/{actions => workflows}/lint.yml | 0 pyproject.toml | 6 +++--- src/doodles/__init__.py | 2 +- src/doodles/doodles.py | 10 +++++----- src/doodles/draw_engine.py | 7 +++---- src/doodles/lines.py | 10 +++++----- src/doodles/main.py | 3 ++- src/doodles/py.typed | 0 src/doodles/shapes.py | 19 ++++++++++--------- src/doodles/world.py | 7 ++++--- 10 files changed, 33 insertions(+), 31 deletions(-) rename .github/{actions => workflows}/lint.yml (100%) create mode 100644 src/doodles/py.typed diff --git a/.github/actions/lint.yml b/.github/workflows/lint.yml similarity index 100% rename from .github/actions/lint.yml rename to .github/workflows/lint.yml diff --git a/pyproject.toml b/pyproject.toml index c2ce639..fd23284 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ mypy = "^1.10.0" requires = ["poetry-core"] build-backend = "poetry.core.masonry.api" -[mypy] -ignore = [ - "src/doodles/examples/*" +[tool.mypy] +exclude = [ + "src/doodles/examples/" ] diff --git a/src/doodles/__init__.py b/src/doodles/__init__.py index 9e520b5..9ef8b52 100644 --- a/src/doodles/__init__.py +++ b/src/doodles/__init__.py @@ -24,4 +24,4 @@ from .color import Color from .text import Text -__all__ = [Doodle, Group, Line, Circle, Rectangle, Color, Text] +__all__ = ["Doodle", "Group", "Line", "Circle", "Rectangle", "Color", "Text"] diff --git a/src/doodles/doodles.py b/src/doodles/doodles.py index 9aba6a8..889cdf4 100644 --- a/src/doodles/doodles.py +++ b/src/doodles/doodles.py @@ -41,7 +41,7 @@ class Doodle(ABC): _updates: list[Callable] _color: tuple[int, int, int] _alpha: int - _z_index: int + _z_index: float def __init__(self, parent=None): # To avoid all child constructors having an ever-expanding @@ -258,14 +258,14 @@ class Doodle(ABC): return self._pos_vec[1] @property - def world_vec(self) -> (float, float): + def world_vec(self) -> tuple[float, float]: """ Obtain derived position vector as a 2-tuple. """ return self.world_x, self.world_y @property - def rgba(self) -> (int, int, int, int): + def rgba(self) -> tuple[int, int, int, int]: """ Access for color+alpha, used by draw functions which need a 4-tuple. @@ -321,7 +321,7 @@ class Group(Doodle): """ pass - def copy(self) -> "Group": + def copy(self) -> Self: """ An override of copy that handles the special case of having a mutable list of Doodles @@ -340,7 +340,7 @@ class Group(Doodle): child._register() return new - def color(self, color: tuple[int, int, int]) -> Doodle: + def color(self, color: tuple[int, int, int]) -> Self: """ An override of Doodle.color. diff --git a/src/doodles/draw_engine.py b/src/doodles/draw_engine.py index 4c9967f..a8bbc80 100644 --- a/src/doodles/draw_engine.py +++ b/src/doodles/draw_engine.py @@ -3,14 +3,12 @@ from typing import TYPE_CHECKING # this is needed because of circular references if TYPE_CHECKING: - from .colors import Color from .doodles import Doodle from .shapes import Rectangle, Circle from .lines import Line from .text import Text - class DrawEngine(abc.ABC): """ This is an abstract class that defines the methods needed @@ -36,6 +34,7 @@ class DrawEngine(abc.ABC): isolation (such as a library you want to avoid tight coupling to) only is added to a specific class or module. """ + @abc.abstractmethod def init(self): """ @@ -44,7 +43,7 @@ class DrawEngine(abc.ABC): """ @abc.abstractmethod - def render(self, background_color: "Color", drawables: list["Doodle"]): + def render(self, background_color: tuple[int, int, int], drawables: list["Doodle"]): """ Workhorse function, should set background and then draw all Doodles. @@ -70,7 +69,7 @@ class DrawEngine(abc.ABC): """ @abc.abstractmethod - def text_render(self, text: "Text"): + def text_render(self, text: str, font: str, color: tuple[int, int, int]): """ Method to pre-render a text object. """ diff --git a/src/doodles/lines.py b/src/doodles/lines.py index 501802a..66bc4db 100644 --- a/src/doodles/lines.py +++ b/src/doodles/lines.py @@ -6,7 +6,7 @@ the easiest to learn from. """ import math import random -from typing import Callable +from typing import Callable, Self from .doodles import Doodle from .world import world @@ -50,7 +50,7 @@ class Line(Doodle): ## Setters / Modifiers / Getters ############## - def to(self, x: float, y: float) -> "Doodle": + def to(self, x: float, y: float) -> Self: """ A setter for the line's offset vector. @@ -63,7 +63,7 @@ class Line(Doodle): self._offset_vec = (x, y) return self - def vec(self, degrees: float, magnitude: float): + def vec(self, degrees: float | Callable, magnitude: float): """ Alternate setter, to create offset vector from angle & length. @@ -72,7 +72,7 @@ class Line(Doodle): directly (`to`), but there is also an alternate option that handles commonly used case. """ - if isinstance(degrees, Callable): + if callable(degrees): self.register_update( self.to, lambda: magnitude * math.cos(math.radians(degrees())), @@ -85,7 +85,7 @@ class Line(Doodle): magnitude * math.sin(math.radians(degrees)), ) - def random(self) -> "Doodle": + def random(self) -> Self: """ Overrides the parent's random, since a random line also needs to have a offset vector. diff --git a/src/doodles/main.py b/src/doodles/main.py index a3bc614..711d1b4 100644 --- a/src/doodles/main.py +++ b/src/doodles/main.py @@ -16,6 +16,7 @@ from pathlib import Path import pygame import importlib import typer +from typing import Optional from .world import world @@ -54,7 +55,7 @@ def load_module(modname: str): return mod.create() -def main(modname: str = None): +def main(modname: Optional[str] = None): """ Entrypoint method. diff --git a/src/doodles/py.typed b/src/doodles/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/doodles/shapes.py b/src/doodles/shapes.py index aecc614..1a4c5be 100644 --- a/src/doodles/shapes.py +++ b/src/doodles/shapes.py @@ -5,6 +5,7 @@ these classes only differ from `Line` in implementation. The interface & decisions are the same but specific to `Circle` and `Rectangle`. """ +from typing import Self import random from .doodles import Doodle from .world import world @@ -23,7 +24,7 @@ class Circle(Doodle): # TODO: do we need to override draw? can we move this to Doodle.draw world.draw_engine.circle_draw(self) - def radius(self, r: float) -> "Doodle": + def radius(self, r: float) -> Self: """ A setter for the circle's radius. """ @@ -40,7 +41,7 @@ class Circle(Doodle): """ return self.radius(self._radius + by) - def random(self) -> "Doodle": + def random(self) -> Self: super().random() # constrain to 10-100 return self.radius(random.random() * 90 + 10) @@ -62,26 +63,26 @@ class Rectangle(Doodle): def draw(self): world.draw_engine.rect_draw(self) - def width(self, w: float) -> "Doodle": + def width(self, w: float) -> Self: """ Set new width. """ self._width = w return self - def height(self, h: float) -> "Doodle": + def height(self, h: float) -> Self: """ Set new height. """ self._height = h return self - def grow(self, dw: float, dh: float): - return self.width(self._w + dw).height(self._h + dh) + def grow(self, dw: float, dh: float) -> Self: + return self.width(self._width + dw).height(self._height + dh) - def random(self, upper=100) -> "Doodle": + def random(self, size: float = 100) -> Self: super().random() # constrain to 10-100 - return self.width(random.random() * upper + 10).height( - random.random() * upper + 10 + return self.width(random.random() * size + 10).height( + random.random() * size + 10 ) diff --git a/src/doodles/world.py b/src/doodles/world.py index 194dfd0..f02f144 100644 --- a/src/doodles/world.py +++ b/src/doodles/world.py @@ -16,6 +16,7 @@ if TYPE_CHECKING: from .lines import Line from .text import Text + class PygameDrawEngine(DrawEngine): # Having each bit of text on the screen load a separate copy # of its font would be wasteful, since the most common case would @@ -66,7 +67,7 @@ class PygameDrawEngine(DrawEngine): world.draw_engine.make_font("medium", 24, "copperplate") world.draw_engine.make_font("large", 48, "papyrus") - def render(self, background_color: Color, drawables: list["Doodle"]): + def render(self, background_color: tuple[int, int, int], drawables: list["Doodle"]): self.buffer.fill((*background_color, 255)) for d in sorted(drawables, key=lambda d: d._z_index): d.draw() @@ -89,8 +90,8 @@ class PygameDrawEngine(DrawEngine): def line_draw(self, ll: "Line"): pygame.draw.aaline(self.buffer, ll.rgba, ll.world_vec, ll.end_vec) - # TODO: return type? - def text_render(self, text: str, font: str, color: Color): + # TODO: hard to type, revisit + def text_render(self, text: str, font, color: tuple[int, int, int]): """returns an intermediated RenderedText""" return font.render(text, True, color)