From a41a47c6c9ba72db6c9c3881e720a84fbebf67ef Mon Sep 17 00:00:00 2001 From: Jaskrendix Date: Wed, 24 Jan 2024 19:38:28 +0100 Subject: [PATCH] typehints --- pytmx/pytmx.py | 44 +++++++++++++++++++++++++------------------- pytmx/util_pygame.py | 38 ++++++++++++++++++++------------------ 2 files changed, 45 insertions(+), 37 deletions(-) diff --git a/pytmx/pytmx.py b/pytmx/pytmx.py index cc06665..35b73a4 100644 --- a/pytmx/pytmx.py +++ b/pytmx/pytmx.py @@ -32,7 +32,7 @@ from itertools import chain, product from math import cos, radians, sin from operator import attrgetter -from typing import DefaultDict, Optional, Union +from typing import Any, Optional, Union from xml.etree import ElementTree # for type hinting @@ -518,7 +518,9 @@ def __init__( # only used tiles are actually loaded, so there will be a difference # between the GIDs in the Tiled map data (tmx) and the data in this # object and the layers. This dictionary keeps track of that. - self.gidmap: DefaultDict[int, list[tuple[int, TileFlags]]] = defaultdict(list) + self.gidmap: defaultdict[ + int, list[tuple[int, Optional[TileFlags]]] + ] = defaultdict(list) # mapping of gid and trans flags to real gids self.imagemap: dict[tuple[int, TileFlags], tuple[int, TileFlags]] = {} # mapping of tiledgid to pytmx gid @@ -546,7 +548,7 @@ def __init__( self.custom_types = dict() # initialize the gid mapping - self.imagemap[(0, TileFlags(0, 0, 0))] = (0, TileFlags(0, 0, 0)) + self.imagemap[(0, empty_flags)] = (0, empty_flags) if custom_property_filename: self.parse_json(json.load(open(custom_property_filename))) @@ -655,7 +657,7 @@ def reload_images(self) -> None: # skip tilesets without a source if ts.source is None: continue - + assert self.filename path = os.path.join(os.path.dirname(self.filename), ts.source) colorkey = getattr(ts, "trans", None) loader = self.image_loader(path, colorkey, tileset=ts) @@ -699,7 +701,7 @@ def reload_images(self) -> None: # load image layer images for layer in (i for i in self.layers if isinstance(i, TiledImageLayer)): source = getattr(layer, "source", None) - if source: + if source and self.filename: colorkey = getattr(layer, "trans", None) real_gid = len(self.images) gid = self.register_gid(real_gid) @@ -714,7 +716,7 @@ def reload_images(self) -> None: # was loaded from the tileset for real_gid, props in self.tile_properties.items(): source = props.get("source", None) - if source: + if source and self.filename: colorkey = props.get("trans", None) path = os.path.join(os.path.dirname(self.filename), source) loader = self.image_loader(path, colorkey) @@ -1109,7 +1111,7 @@ def register_gid( """ if flags is None: - flags = TileFlags(0, 0, 0) + flags = empty_flags if tiled_gid: try: @@ -1148,7 +1150,9 @@ def register_gid_check_flags( else: return self.register_gid(*decode_gid(tiled_gid)) - def map_gid(self, tiled_gid: int) -> Optional[list[tuple[int, TileFlags]]]: + def map_gid( + self, tiled_gid: int + ) -> Optional[list[tuple[int, Optional[TileFlags]]]]: """Used to lookup a GID read from a TMX file's data. Args: @@ -1199,8 +1203,8 @@ def __init__(self, parent: TiledMap, node: ElementTree.Element) -> None: # defaults from the specification self.firstgid = 0 - self.source = None - self.name = None + self.source: Optional[str] = None + self.name: Optional[str] = None self.tilewidth = 0 self.tileheight = 0 self.spacing = 0 @@ -1231,7 +1235,7 @@ def parse_xml(self, node: ElementTree.Element) -> TiledTileset: # if true, then node references an external tileset source = node.get("source", None) if source: - if source[-4:].lower() == ".tsx": + if source[-4:].lower() == ".tsx" and self.parent.filename: # external tilesets don't save this, store it for later self.firstgid = int(node.get("firstgid", 0)) @@ -1253,6 +1257,7 @@ def parse_xml(self, node: ElementTree.Element) -> TiledTileset: logger.error(msg.format(path)) raise Exception(msg.format(path)) from io else: + assert self.source msg = "Found external tileset, but cannot handle type: {0}" logger.error(msg.format(self.source)) raise Exception(msg.format(self.source)) @@ -1278,18 +1283,18 @@ def parse_xml(self, node: ElementTree.Element) -> TiledTileset: p["width"] = str(self.tilewidth) p["height"] = str(self.tileheight) else: - tile_source = image.get("source") + tile_source = image.get("source", "") # images are listed as relative to the .tsx file, not the .tmx file: if source and tile_source: tile_source = os.path.join(os.path.dirname(source), tile_source) p["source"] = tile_source - p["trans"] = image.get("trans", None) + p["trans"] = image.get("trans", "") p["width"] = image.get("width", "0") p["height"] = image.get("height", "0") # handle tiles with animations anim = child.find("animation") - frames = list() + frames: list[AnimationFrame] = [] p["frames"] = frames if anim is not None: for frame in anim.findall("frame"): @@ -1339,7 +1344,7 @@ def __init__(self, parent: TiledMap, node: ElementTree.Element) -> None: """ TiledElement.__init__(self) self.parent = parent - self.name = None + self.name: Optional[str] = None self.visible = True self.parse_xml(node) @@ -1372,7 +1377,7 @@ def __init__(self, parent: TiledMap, node: ElementTree.Element) -> None: self.data: list[list[int]] = [] # defaults from the specification - self.name = None + self.name: Optional[str] = None self.width = 0 self.height = 0 self.opacity = 1.0 @@ -1444,6 +1449,7 @@ def parse_xml(self, node: ElementTree.Element) -> TiledTileLayer: "XML tile elements are no longer supported. Must use base64 or csv map formats." ) + assert data_node.text temp = [ self.parent.register_gid_check_flags(gid) for gid in unpack_gids( @@ -1535,7 +1541,7 @@ def __init__( self.parse_xml(node) @property - def image(self): + def image(self) -> Optional[Any]: """Image for the object, if assigned. Returns: @@ -1634,14 +1640,14 @@ def __init__(self, parent: TiledMap, node: ElementTree.Element) -> None: self.gid = 0 # defaults from the specification - self.name = None + self.name: Optional[str] = None self.opacity = 1 self.visible = True self.parse_xml(node) @property - def image(self): + def image(self) -> Optional[Any]: """Image for the object, if assigned. Returns: diff --git a/pytmx/util_pygame.py b/pytmx/util_pygame.py index d563586..5b6ea95 100644 --- a/pytmx/util_pygame.py +++ b/pytmx/util_pygame.py @@ -19,10 +19,11 @@ """ import itertools import logging -from typing import Optional, Union +from collections.abc import Callable +from typing import Any, Optional, Union import pytmx -from pytmx.pytmx import ColorLike, PointLike +from pytmx.pytmx import ColorLike, TileFlags logger = logging.getLogger(__name__) @@ -37,8 +38,7 @@ def handle_transformation( - tile: pygame.Surface, - flags: pytmx.TileFlags, + tile: pygame.Surface, flags: pytmx.TileFlags ) -> pygame.Surface: """ Transform tile according to the flags and return a new one @@ -112,7 +112,9 @@ def smart_convert( return tile -def pygame_image_loader(filename: str, colorkey: Optional[ColorLike], **kwargs): +def pygame_image_loader( + filename: str, colorkey: Optional[ColorLike], **kwargs: Any +) -> Callable[[Optional[pygame.Rect], Optional[TileFlags]], pygame.Surface]: """ pytmx image loader for pygame @@ -130,7 +132,9 @@ def pygame_image_loader(filename: str, colorkey: Optional[ColorLike], **kwargs): pixelalpha = kwargs.get("pixelalpha", True) image = pygame.image.load(filename) - def load_image(rect=None, flags=None): + def load_image( + rect: Optional[pygame.Rect] = None, flags: Optional[TileFlags] = None + ) -> pygame.Surface: if rect: try: tile = image.subsurface(rect) @@ -149,11 +153,7 @@ def load_image(rect=None, flags=None): return load_image -def load_pygame( - filename: str, - *args, - **kwargs, -) -> pytmx.TiledMap: +def load_pygame(filename: str, *args: Any, **kwargs: Any) -> pytmx.TiledMap: """Load a TMX file, images, and return a TiledMap class PYGAME USERS: Use me. @@ -208,7 +208,7 @@ def build_rects( """ if isinstance(tileset, int): try: - tileset = tmxmap.tilesets[tileset] + _tileset = tmxmap.tilesets[tileset] except IndexError: msg = "Tileset #{0} not found in map {1}." logger.debug(msg.format(tileset, tmxmap)) @@ -216,7 +216,7 @@ def build_rects( elif isinstance(tileset, str): try: - tileset = [t for t in tmxmap.tilesets if t.name == tileset].pop() + _tileset = [t for t in tmxmap.tilesets if t.name == tileset].pop() except IndexError: msg = 'Tileset "{0}" not found in map {1}.' logger.debug(msg.format(tileset, tmxmap)) @@ -230,7 +230,9 @@ def build_rects( gid = None if real_gid: try: - gid, flags = tmxmap.map_gid(real_gid)[0] + _map_gid = tmxmap.map_gid(real_gid) + if _map_gid: + gid, flags = _map_gid[0] except IndexError: msg = "GID #{0} not found" logger.debug(msg.format(real_gid)) @@ -240,7 +242,7 @@ def build_rects( layer_data = tmxmap.get_layer_data(layer) elif isinstance(layer, str): try: - _layer = [l for l in tmxmap.layers if l.name == layer].pop() + _layer = [l for l in tmxmap.layers if l.name and l.name == layer].pop() layer_data = _layer.data except IndexError: msg = 'Layer "{0}" not found in map {1}.' @@ -258,7 +260,7 @@ def build_rects( def simplify( - all_points: list[PointLike], + all_points: list[tuple[int, int]], tilewidth: int, tileheight: int, ) -> list[pygame.Rect]: @@ -304,7 +306,7 @@ def simplify( making a list of rects, one for each tile on the map! """ - def pick_rect(points, rects) -> None: + def pick_rect(points: list[tuple[int, int]], rects: list[pygame.Rect]) -> None: ox, oy = sorted([(sum(p), p) for p in points])[0][1] x = ox y = oy @@ -345,7 +347,7 @@ def pick_rect(points, rects) -> None: if points: pick_rect(points, rects) - rect_list = [] + rect_list: list[pygame.Rect] = [] while all_points: pick_rect(all_points, rect_list)