Skip to content

Commit

Permalink
Added type hints
Browse files Browse the repository at this point in the history
  • Loading branch information
radarhere committed Feb 5, 2024
1 parent 9525553 commit 5a8e7dd
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 39 deletions.
2 changes: 1 addition & 1 deletion Tests/test_imagedraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -1519,7 +1519,7 @@ def test_compute_regular_polygon_vertices(
[
(None, (50, 50, 25), 0, TypeError, "n_sides should be an int"),
(1, (50, 50, 25), 0, ValueError, "n_sides should be an int > 2"),
(3, 50, 0, TypeError, "bounding_circle should be a tuple"),
(3, 50, 0, TypeError, "bounding_circle should be a sequence"),
(
3,
(50, 50, 100, 100),
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/Image.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ def close(self):
# object is gone.
self.im = DeferredError(ValueError("Operation on closed image"))

def _copy(self):
def _copy(self) -> None:
self.load()
self.im = self.im.copy()
self.pyaccess = None
Expand Down
73 changes: 37 additions & 36 deletions src/PIL/ImageDraw.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
class ImageDraw:
font = None

def __init__(self, im, mode=None):
def __init__(self, im: Image.Image, mode: str | None = None) -> None:
"""
Create a drawing instance.
Expand Down Expand Up @@ -115,7 +115,7 @@ def getfont(self):
self.font = ImageFont.load_default()
return self.font

def _getfont(self, font_size):
def _getfont(self, font_size: float | None):
if font_size is not None:
from . import ImageFont

Expand All @@ -124,7 +124,7 @@ def _getfont(self, font_size):
font = self.getfont()
return font

def _getink(self, ink, fill=None):
def _getink(self, ink, fill=None) -> tuple[int | None, int | None]:
if ink is None and fill is None:
if self.fill:
fill = self.ink
Expand All @@ -145,13 +145,13 @@ def _getink(self, ink, fill=None):
fill = self.draw.draw_ink(fill)
return ink, fill

def arc(self, xy, start, end, fill=None, width=1):
def arc(self, xy, start, end, fill=None, width=1) -> None:
"""Draw an arc."""
ink, fill = self._getink(fill)
if ink is not None:
self.draw.draw_arc(xy, start, end, ink, width)

def bitmap(self, xy, bitmap, fill=None):
def bitmap(self, xy, bitmap, fill=None) -> None:
"""Draw a bitmap."""
bitmap.load()
ink, fill = self._getink(fill)
Expand All @@ -160,23 +160,23 @@ def bitmap(self, xy, bitmap, fill=None):
if ink is not None:
self.draw.draw_bitmap(xy, bitmap.im, ink)

def chord(self, xy, start, end, fill=None, outline=None, width=1):
def chord(self, xy, start, end, fill=None, outline=None, width=1) -> None:
"""Draw a chord."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_chord(xy, start, end, fill, 1)
if ink is not None and ink != fill and width != 0:
self.draw.draw_chord(xy, start, end, ink, 0, width)

def ellipse(self, xy, fill=None, outline=None, width=1):
def ellipse(self, xy, fill=None, outline=None, width=1) -> None:
"""Draw an ellipse."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_ellipse(xy, fill, 1)
if ink is not None and ink != fill and width != 0:
self.draw.draw_ellipse(xy, ink, 0, width)

def line(self, xy, fill=None, width=0, joint=None):
def line(self, xy, fill=None, width=0, joint=None) -> None:
"""Draw a line, or a connected sequence of line segments."""
ink = self._getink(fill)[0]
if ink is not None:
Expand Down Expand Up @@ -236,7 +236,7 @@ def coord_at_angle(coord, angle):
]
self.line(gap_coords, fill, width=3)

def shape(self, shape, fill=None, outline=None):
def shape(self, shape, fill=None, outline=None) -> None:
"""(Experimental) Draw a shape."""
shape.close()
ink, fill = self._getink(outline, fill)
Expand All @@ -245,29 +245,29 @@ def shape(self, shape, fill=None, outline=None):
if ink is not None and ink != fill:
self.draw.draw_outline(shape, ink, 0)

def pieslice(self, xy, start, end, fill=None, outline=None, width=1):
def pieslice(self, xy, start, end, fill=None, outline=None, width=1) -> None:
"""Draw a pieslice."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_pieslice(xy, start, end, fill, 1)
if ink is not None and ink != fill and width != 0:
self.draw.draw_pieslice(xy, start, end, ink, 0, width)

def point(self, xy, fill=None):
def point(self, xy, fill=None) -> None:
"""Draw one or more individual pixels."""
ink, fill = self._getink(fill)
if ink is not None:
self.draw.draw_points(xy, ink)

def polygon(self, xy, fill=None, outline=None, width=1):
def polygon(self, xy, fill=None, outline=None, width=1) -> None:
"""Draw a polygon."""
ink, fill = self._getink(outline, fill)
if fill is not None:
self.draw.draw_polygon(xy, fill, 1)
if ink is not None and ink != fill and width != 0:
if width == 1:
self.draw.draw_polygon(xy, ink, 0, width)
else:
elif self.im is not None:
# To avoid expanding the polygon outwards,
# use the fill as a mask
mask = Image.new("1", self.im.size)
Expand All @@ -291,12 +291,12 @@ def polygon(self, xy, fill=None, outline=None, width=1):

def regular_polygon(
self, bounding_circle, n_sides, rotation=0, fill=None, outline=None, width=1
):
) -> None:
"""Draw a regular polygon."""
xy = _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation)
self.polygon(xy, fill, outline, width)

def rectangle(self, xy, fill=None, outline=None, width=1):
def rectangle(self, xy, fill=None, outline=None, width=1) -> None:
"""Draw a rectangle."""
ink, fill = self._getink(outline, fill)
if fill is not None:
Expand All @@ -306,7 +306,7 @@ def rectangle(self, xy, fill=None, outline=None, width=1):

def rounded_rectangle(
self, xy, radius=0, fill=None, outline=None, width=1, *, corners=None
):
) -> None:
"""Draw a rounded rectangle."""
if isinstance(xy[0], (list, tuple)):
(x0, y0), (x1, y1) = xy
Expand Down Expand Up @@ -346,7 +346,7 @@ def rounded_rectangle(
r = d // 2
ink, fill = self._getink(outline, fill)

def draw_corners(pieslice):
def draw_corners(pieslice) -> None:
if full_x:
# Draw top and bottom halves
parts = (
Expand Down Expand Up @@ -431,12 +431,12 @@ def draw_corners(pieslice):
right[3] -= r + 1
self.draw.draw_rectangle(right, ink, 1)

def _multiline_check(self, text):
def _multiline_check(self, text) -> bool:
split_character = "\n" if isinstance(text, str) else b"\n"

return split_character in text

def _multiline_split(self, text):
def _multiline_split(self, text) -> list[str | bytes]:
split_character = "\n" if isinstance(text, str) else b"\n"

return text.split(split_character)
Expand Down Expand Up @@ -465,7 +465,7 @@ def text(
embedded_color=False,
*args,
**kwargs,
):
) -> None:
"""Draw text."""
if embedded_color and self.mode not in ("RGB", "RGBA"):
msg = "Embedded color supported only in RGB and RGBA modes"
Expand Down Expand Up @@ -497,7 +497,7 @@ def getink(fill):
return fill
return ink

def draw_text(ink, stroke_width=0, stroke_offset=None):
def draw_text(ink, stroke_width=0, stroke_offset=None) -> None:
mode = self.fontmode
if stroke_width == 0 and embedded_color:
mode = "RGBA"
Expand Down Expand Up @@ -547,7 +547,8 @@ def draw_text(ink, stroke_width=0, stroke_offset=None):
ink_alpha = struct.pack("i", ink)[3]
color.fillband(3, ink_alpha)
x, y = coord
self.im.paste(color, (x, y, x + mask.size[0], y + mask.size[1]), mask)
if self.im is not None:
self.im.paste(color, (x, y, x + mask.size[0], y + mask.size[1]), mask)
else:
self.draw.draw_bitmap(coord, mask, ink)

Expand Down Expand Up @@ -584,7 +585,7 @@ def multiline_text(
embedded_color=False,
*,
font_size=None,
):
) -> None:
if direction == "ttb":
msg = "ttb direction is unsupported for multiline text"
raise ValueError(msg)
Expand Down Expand Up @@ -693,7 +694,7 @@ def textbbox(
embedded_color=False,
*,
font_size=None,
):
) -> tuple[int, int, int, int]:
"""Get the bounding box of a given string, in pixels."""
if embedded_color and self.mode not in ("RGB", "RGBA"):
msg = "Embedded color supported only in RGB and RGBA modes"
Expand Down Expand Up @@ -738,7 +739,7 @@ def multiline_textbbox(
embedded_color=False,
*,
font_size=None,
):
) -> tuple[int, int, int, int]:
if direction == "ttb":
msg = "ttb direction is unsupported for multiline text"
raise ValueError(msg)
Expand Down Expand Up @@ -777,7 +778,7 @@ def multiline_textbbox(
elif anchor[1] == "d":
top -= (len(lines) - 1) * line_spacing

bbox = None
bbox: tuple[int, int, int, int] | None = None

for idx, line in enumerate(lines):
left = xy[0]
Expand Down Expand Up @@ -828,7 +829,7 @@ def multiline_textbbox(
return bbox


def Draw(im, mode=None):
def Draw(im: Image.Image, mode: str | None = None) -> ImageDraw:
"""
A simple 2D drawing interface for PIL images.
Expand Down Expand Up @@ -876,7 +877,7 @@ def getdraw(im=None, hints=None):
return im, handler


def floodfill(image, xy, value, border=None, thresh=0):
def floodfill(image: Image.Image, xy, value, border=None, thresh=0) -> None:
"""
(experimental) Fills a bounded region with a given color.
Expand Down Expand Up @@ -932,7 +933,7 @@ def floodfill(image, xy, value, border=None, thresh=0):
edge = new_edge


def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation):
def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation) -> list[tuple[float, float]]:
"""
Generate a list of vertices for a 2D regular polygon.
Expand Down Expand Up @@ -982,7 +983,7 @@ def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation):

# 1.2 Check `bounding_circle` has an appropriate value
if not isinstance(bounding_circle, (list, tuple)):
msg = "bounding_circle should be a tuple"
msg = "bounding_circle should be a sequence"
raise TypeError(msg)

if len(bounding_circle) == 3:
Expand Down Expand Up @@ -1014,7 +1015,7 @@ def _compute_regular_polygon_vertices(bounding_circle, n_sides, rotation):
raise ValueError(msg)

# 2. Define Helper Functions
def _apply_rotation(point, degrees, centroid):
def _apply_rotation(point: list[float], degrees: float) -> tuple[int, int]:
return (
round(
point[0] * math.cos(math.radians(360 - degrees))
Expand All @@ -1030,11 +1031,11 @@ def _apply_rotation(point, degrees, centroid):
),
)

def _compute_polygon_vertex(centroid, polygon_radius, angle):
def _compute_polygon_vertex(angle: float) -> tuple[int, int]:
start_point = [polygon_radius, 0]
return _apply_rotation(start_point, angle, centroid)
return _apply_rotation(start_point, angle)

def _get_angles(n_sides, rotation):
def _get_angles(n_sides: int, rotation: float) -> list[float]:
angles = []
degrees = 360 / n_sides
# Start with the bottom left polygon vertex
Expand All @@ -1051,11 +1052,11 @@ def _get_angles(n_sides, rotation):

# 4. Compute Vertices
return [
_compute_polygon_vertex(centroid, polygon_radius, angle) for angle in angles
_compute_polygon_vertex(angle) for angle in angles
]


def _color_diff(color1, color2):
def _color_diff(color1, color2: float | tuple[int, ...]) -> float:
"""
Uses 1-norm distance to calculate difference between two values.
"""
Expand Down
2 changes: 1 addition & 1 deletion src/PIL/ImageFont.py
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@ def load_path(filename):
raise OSError(msg)


def load_default(size=None):
def load_default(size: float | None = None) -> FreeTypeFont | ImageFont:
"""If FreeType support is available, load a version of Aileron Regular,
https://dotcolon.net/font/aileron, with a more limited character set.
Expand Down

0 comments on commit 5a8e7dd

Please sign in to comment.