From 91e22e6d68d3dfb889b9b180019b4c407710ccbe Mon Sep 17 00:00:00 2001 From: Jaskrendix Date: Tue, 16 Jan 2024 15:08:57 +0100 Subject: [PATCH] py39 --- .github/workflows/test.yml | 5 +--- apps/benchmark.py | 2 +- apps/pygame_demo.py | 2 +- apps/pygame_sdl2_demo.py | 2 +- apps/pyglet_demo.py | 2 +- apps/pysdl2_demo.py | 2 +- docs/conf.py | 6 ++--- pyproject.toml | 6 ++--- pytmx/__init__.py | 5 +--- pytmx/pytmx.py | 51 ++++++++++++++++++----------------- pytmx/util_pygame.py | 10 +++---- pytmx/util_pygame_sdl2.py | 12 +++++---- pytmx/util_pyglet.py | 2 +- pytmx/util_pysdl2.py | 2 +- readme.md | 2 +- tests/pytmx/test_pytmx.py | 54 +++++++++++++++++++------------------- 16 files changed, 79 insertions(+), 86 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ba5dd1c..6048d2b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,10 +13,7 @@ jobs: runs-on: [ubuntu-latest] strategy: matrix: - python-version: - - 3.7 - - 3.8 - - 3.9 + python-version: ['3.9', '3.10', '3.11', '3.12'] dependencies: - pygame pyglet - pygame diff --git a/apps/benchmark.py b/apps/benchmark.py index 9ae33e5..785acd1 100644 --- a/apps/benchmark.py +++ b/apps/benchmark.py @@ -1,6 +1,6 @@ """ This is tested on pygame 1.9 and python 2.7 and 3.3+. -Leif Theden "bitcraft", 2012-2022 +Leif Theden "bitcraft", 2012-2024 Rendering demo for the TMXLoader. diff --git a/apps/pygame_demo.py b/apps/pygame_demo.py index 0addcdf..6084b2d 100644 --- a/apps/pygame_demo.py +++ b/apps/pygame_demo.py @@ -1,6 +1,6 @@ """ This is tested on pygame 1.9 and python 2.7 and 3.3+. -Leif Theden "bitcraft", 2012-2023 +Leif Theden "bitcraft", 2012-2024 Rendering demo for the TMXLoader. diff --git a/apps/pygame_sdl2_demo.py b/apps/pygame_sdl2_demo.py index 3eb69ca..35c341c 100644 --- a/apps/pygame_sdl2_demo.py +++ b/apps/pygame_sdl2_demo.py @@ -1,6 +1,6 @@ """ This is tested on pygame 2.0.1 and python 3.9.6. -Leif Theden "bitcraft", 2012-2022 +Leif Theden "bitcraft", 2012-2024 Rendering demo for the TMXLoader. diff --git a/apps/pyglet_demo.py b/apps/pyglet_demo.py index 82fdd4e..de82716 100644 --- a/apps/pyglet_demo.py +++ b/apps/pyglet_demo.py @@ -1,6 +1,6 @@ """ This is tested on pyglet 1.2 and python 2.7. -Leif Theden "bitcraft", 2012-2022 +Leif Theden "bitcraft", 2012-2024 Rendering demo for the TMXLoader. diff --git a/apps/pysdl2_demo.py b/apps/pysdl2_demo.py index 8d67da2..0c15fff 100644 --- a/apps/pysdl2_demo.py +++ b/apps/pysdl2_demo.py @@ -1,6 +1,6 @@ """ This is tested on pysdl2 1.2 and python 2.7. -Leif Theden "bitcraft", 2012-2022 +Leif Theden "bitcraft", 2012-2024 Rendering demo for the TMXLoader. diff --git a/docs/conf.py b/docs/conf.py index 1c8959e..3f16112 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -18,10 +18,10 @@ # -- Project information ----------------------------------------------------- -from typing import Any, List +from typing import Any project = "pytmx" -copyright = "2012-2021, Leif Theden" +copyright = "2012-2024, Leif Theden" author = "Leif Theden" # The full version, including alpha/beta/rc tags @@ -72,7 +72,7 @@ # Apidoc call to generate automatic reference docs. Taken from # https://github.com/readthedocs/readthedocs.org/issues/1139#issuecomment-398083449 def run_apidoc(_: Any) -> None: - ignore_paths: List[str] = [] + ignore_paths: list[str] = [] argv = ["-f", "-e", "-M", "-o", "autogen", ".."] + ignore_paths diff --git a/pyproject.toml b/pyproject.toml index 7f1e51f..7ec4cd6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,8 +15,6 @@ classifiers = [ "Intended Audience :: Developers", "Development Status :: 5 - Production/Stable", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", @@ -25,7 +23,7 @@ classifiers = [ "Topic :: Multimedia :: Graphics", "Topic :: Software Development :: Libraries :: pygame", ] -requires-python = ">=3.7" +requires-python = ">=3.9" [project.urls] source = "https://github.com/bitcraft/PyTMX" @@ -39,7 +37,7 @@ pygame-ce = ["pygame-ce>=2.1.3"] [tool.black] line-length = 88 -target-version = ["py37"] +target-version = ["py39"] [tool.isort] line_length = 88 diff --git a/pytmx/__init__.py b/pytmx/__init__.py index ba40067..056a051 100644 --- a/pytmx/__init__.py +++ b/pytmx/__init__.py @@ -1,5 +1,5 @@ """ -Copyright (C) 2012-2023, Leif Theden +Copyright (C) 2012-2024, Leif Theden This file is part of pytmx. @@ -28,6 +28,3 @@ logger.debug("cannot import pygame tools") __version__ = (3, 32) -__author__ = "bitcraft" -__author_email__ = "leif.theden@gmail.com" -__description__ = "Map loader for TMX Files - Python 3.3 +" diff --git a/pytmx/pytmx.py b/pytmx/pytmx.py index 92ffb0c..0dda7ee 100644 --- a/pytmx/pytmx.py +++ b/pytmx/pytmx.py @@ -1,5 +1,5 @@ """ -Copyright (C) 2012-2023, Leif Theden +Copyright (C) 2012-2024, Leif Theden This file is part of pytmx. @@ -27,11 +27,12 @@ import zlib from base64 import b64decode from collections import defaultdict, namedtuple +from collections.abc import Iterable, Sequence from copy import deepcopy from itertools import chain, product from math import cos, radians, sin from operator import attrgetter -from typing import Dict, Iterable, List, Optional, Sequence, Tuple, Union +from typing import Optional, Union from xml.etree import ElementTree # for type hinting @@ -79,17 +80,17 @@ Point = namedtuple("Point", ["x", "y"]) TileFlags = namedtuple("TileFlags", flag_names) empty_flags = TileFlags(False, False, False) -ColorLike = Union[Tuple[int, int, int, int], Tuple[int, int, int], int, str] -MapPoint = Tuple[int, int, int] +ColorLike = Union[tuple[int, int, int, int], tuple[int, int, int], int, str] +MapPoint = tuple[int, int, int] TiledLayer = Union[ "TiledTileLayer", "TiledImageLayer", "TiledGroupLayer", "TiledObjectGroup" ] # need a more graceful way to handle annotations for optional dependencies if pygame: - PointLike = Union[Tuple[int, int], pygame.Vector2, Point] + PointLike = Union[tuple[int, int], pygame.Vector2, Point] else: - PointLike = Union[Tuple[int, int], Point] + PointLike = Union[tuple[int, int], Point] def default_image_loader(filename: str, flags, **kwargs): @@ -112,7 +113,7 @@ def load(rect=None, flags=None): return load -def decode_gid(raw_gid: int) -> Tuple[int, TileFlags]: +def decode_gid(raw_gid: int) -> tuple[int, TileFlags]: """Decode a GID from TMX data. Args: @@ -136,9 +137,9 @@ def decode_gid(raw_gid: int) -> Tuple[int, TileFlags]: def reshape_data( - gids: List[int], + gids: list[int], width: int, -) -> List[List[int]]: +) -> list[list[int]]: """Change 1D list to 2d list Args: @@ -156,7 +157,7 @@ def unpack_gids( text: str, encoding: Optional[str] = None, compression: Optional[str] = None, -) -> List[int]: +) -> list[int]: """Return all gids from encoded/compressed layer data Args: @@ -184,7 +185,7 @@ def unpack_gids( raise ValueError(f"layer encoding {encoding} is not supported.") -def convert_to_bool(value: str) -> bool: +def convert_to_bool(value: Optional[Union[str, int, float]] = None) -> bool: """Convert a few common variations of "true" and "false" to boolean Args: @@ -230,7 +231,7 @@ def rotate( points: Sequence[Point], origin: Point, angle: Union[int, float], -) -> List[Point]: +) -> list[Point]: """Rotate a sequence of points around an axis. Args: @@ -330,7 +331,7 @@ def rotate( } -def parse_properties(node: ElementTree.Element, customs: dict = None) -> Dict: +def parse_properties(node: ElementTree.Element, customs: dict = None) -> dict: """Parse a Tiled xml node and return a dict. Args: @@ -458,7 +459,7 @@ def __repr__(self): class TiledClassType: """Contains custom Tiled types.""" - def __init__(self, name: str, members: List[dict]) -> None: + def __init__(self, name: str, members: list[dict]) -> None: """Creates the TiledClassType. Args: @@ -477,7 +478,7 @@ class TiledMap(TiledElement): def __init__( self, filename: Optional[str] = None, - custom_property_filename: Optional[List[str]] = None, + custom_property_filename: Optional[list[str]] = None, image_loader=default_image_loader, **kwargs, ) -> None: @@ -809,7 +810,7 @@ def get_tile_gid(self, x: int, y: int, layer: int) -> int: logger.debug(msg.format(x, y, layer)) raise ValueError(msg.format(x, y, layer)) - def get_tile_properties(self, x: int, y: int, layer: int) -> Optional[Dict]: + def get_tile_properties(self, x: int, y: int, layer: int) -> Optional[dict]: """Return the tile image GID for this location. Args: @@ -864,7 +865,7 @@ def get_tile_locations_by_gid(self, gid: int) -> Iterable[MapPoint]: for x, y, _gid in [i for i in self.layers[l].iter_data() if i[2] == gid]: yield x, y, l - def get_tile_properties_by_gid(self, gid: int) -> Optional[Dict]: + def get_tile_properties_by_gid(self, gid: int) -> Optional[dict]: """Get the tile properties of a tile GID. Args: @@ -1009,7 +1010,7 @@ def get_tileset_from_gid(self, gid: int) -> TiledTileset: raise ValueError("Tileset not found") - def get_tile_colliders(self) -> Iterable[Tuple[int, List[Dict]]]: + def get_tile_colliders(self) -> Iterable[tuple[int, list[dict]]]: """Return iterator of (gid, dict) pairs of tiles with colliders. Returns: @@ -1021,7 +1022,7 @@ def get_tile_colliders(self) -> Iterable[Tuple[int, List[Dict]]]: if colliders: yield gid, colliders - def pixels_to_tile_pos(self, position: Tuple[int, int]) -> Tuple[int, int]: + def pixels_to_tile_pos(self, position: tuple[int, int]) -> tuple[int, int]: return int(position[0] / self.tilewidth), int(position[1] / self.tileheight) @property @@ -1138,7 +1139,7 @@ def register_gid_check_flags( else: return self.register_gid(*decode_gid(tiled_gid)) - def map_gid(self, tiled_gid: int) -> Optional[List[int]]: + def map_gid(self, tiled_gid: int) -> Optional[list[int]]: """Used to lookup a GID read from a TMX file's data. Args: @@ -1157,7 +1158,7 @@ def map_gid(self, tiled_gid: int) -> Optional[List[int]]: logger.debug(msg) raise TypeError(msg) - def map_gid2(self, tiled_gid: int) -> List[Tuple[int, Optional[int]]]: + def map_gid2(self, tiled_gid: int) -> list[tuple[int, Optional[int]]]: """WIP. need to refactor the gid code""" tiled_gid = int(tiled_gid) @@ -1375,7 +1376,7 @@ def __init__(self, parent, node) -> None: def __iter__(self): return self.iter_data() - def iter_data(self) -> Iterable[Tuple[int, int, int]]: + def iter_data(self) -> Iterable[tuple[int, int, int]]: """Yields X, Y, GID tuples for each tile in the layer. Returns: @@ -1574,7 +1575,7 @@ def read_points(text): return self - def apply_transformations(self) -> List[Point]: + def apply_transformations(self) -> list[Point]: """Return all points for object, taking in account rotation.""" if hasattr(self, "points"): return rotate(self.points, self, self.rotation) @@ -1582,9 +1583,7 @@ def apply_transformations(self) -> List[Point]: return rotate(self.as_points, self, self.rotation) @property - def as_points( - self, - ) -> List[Point]: + def as_points(self) -> list[Point]: return [ Point(*i) for i in [ diff --git a/pytmx/util_pygame.py b/pytmx/util_pygame.py index 497058d..a1b5d3d 100644 --- a/pytmx/util_pygame.py +++ b/pytmx/util_pygame.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Copyright (C) 2012-2023, Leif Theden +Copyright (C) 2012-2024, Leif Theden This file is part of pytmx. @@ -19,7 +19,7 @@ """ import itertools import logging -from typing import List, Optional, Union +from typing import Optional, Union import pytmx from pytmx.pytmx import ColorLike, PointLike @@ -188,7 +188,7 @@ def build_rects( layer: Union[int, str], tileset: Optional[Union[int, str]], real_gid: Optional[int], -) -> List[pygame.Rect]: +) -> list[pygame.Rect]: """ Generate a set of non-overlapping rects that represents the distribution of the specified gid. @@ -258,10 +258,10 @@ def build_rects( def simplify( - all_points: List[PointLike], + all_points: list[PointLike], tilewidth: int, tileheight: int, -) -> List[pygame.Rect]: +) -> list[pygame.Rect]: """Given a list of points, return list of rects that represent them kludge: diff --git a/pytmx/util_pygame_sdl2.py b/pytmx/util_pygame_sdl2.py index a2a8e77..48e389a 100644 --- a/pytmx/util_pygame_sdl2.py +++ b/pytmx/util_pygame_sdl2.py @@ -1,5 +1,5 @@ """ -Copyright (C) 2012-2022, Leif Theden +Copyright (C) 2012-2024, Leif Theden This file is part of pytmx. @@ -19,7 +19,7 @@ import dataclasses import logging from functools import partial -from typing import Tuple +from typing import Any, Optional from pygame.rect import Rect @@ -39,14 +39,14 @@ class PygameSDL2Tile: texture: Texture srcrect: Rect - size: Tuple[int, int] + size: tuple[int, int] angle: float = 0.0 center: None = None flipx: bool = False flipy: bool = False -def handle_flags(flags: pytmx.TileFlags): +def handle_flags(flags: Optional[pytmx.TileFlags]) -> tuple[float, bool, bool]: """ Return angle and flip values for the SDL2 renderer @@ -94,7 +94,9 @@ def load_image(rect=None, flags=None) -> PygameSDL2Tile: return load_image -def load_pygame_sdl2(renderer: Renderer, filename: str, *args, **kwargs): +def load_pygame_sdl2( + renderer: Renderer, filename: str, *args: Any, **kwargs: Any +) -> pytmx.TiledMap: """ Load a TMX file, images, and return a TiledMap class diff --git a/pytmx/util_pyglet.py b/pytmx/util_pyglet.py index 906b7e5..163599f 100644 --- a/pytmx/util_pyglet.py +++ b/pytmx/util_pyglet.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Copyright (C) 2012-2017, Leif Theden +Copyright (C) 2012-2024, Leif Theden This file is part of pytmx. diff --git a/pytmx/util_pysdl2.py b/pytmx/util_pysdl2.py index 872e9e4..7c2de85 100644 --- a/pytmx/util_pysdl2.py +++ b/pytmx/util_pysdl2.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- """ -Copyright (C) 2012-2017, Leif Theden +Copyright (C) 2012-2024, Leif Theden This file is part of pytmx. diff --git a/readme.md b/readme.md index f52888f..34ae48f 100644 --- a/readme.md +++ b/readme.md @@ -13,7 +13,7 @@ A map loader for Python/pygame, designed for video games About =============================================================================== -**For Python 3.7+** +**For Python 3.9+** A map loader for python/pygame designed for games. It provides smart tile loading with a fast and efficient storage base. Not only does it correctly handle most Tiled object types, it also will load metadata for diff --git a/tests/pytmx/test_pytmx.py b/tests/pytmx/test_pytmx.py index 3738a2b..9ab99ef 100644 --- a/tests/pytmx/test_pytmx.py +++ b/tests/pytmx/test_pytmx.py @@ -5,7 +5,7 @@ class TestConvertToBool(unittest.TestCase): - def test_string_string_true(self): + def test_string_string_true(self) -> None: self.assertTrue(convert_to_bool("1")) self.assertTrue(convert_to_bool("y")) self.assertTrue(convert_to_bool("Y")) @@ -18,7 +18,7 @@ def test_string_string_true(self): self.assertTrue(convert_to_bool("True")) self.assertTrue(convert_to_bool("TRUE")) - def test_string_string_false(self): + def test_string_string_false(self) -> None: self.assertFalse(convert_to_bool("0")) self.assertFalse(convert_to_bool("n")) self.assertFalse(convert_to_bool("N")) @@ -31,36 +31,36 @@ def test_string_string_false(self): self.assertFalse(convert_to_bool("False")) self.assertFalse(convert_to_bool("FALSE")) - def test_string_number_true(self): + def test_string_number_true(self) -> None: self.assertTrue(convert_to_bool(1)) self.assertTrue(convert_to_bool(1.0)) - def test_string_number_false(self): + def test_string_number_false(self) -> None: self.assertFalse(convert_to_bool(0)) self.assertFalse(convert_to_bool(0.0)) self.assertFalse(convert_to_bool(-1)) self.assertFalse(convert_to_bool(-1.1)) - def test_string_bool_true(self): + def test_string_bool_true(self) -> None: self.assertTrue(convert_to_bool(True)) - def test_string_bool_false(self): + def test_string_bool_false(self) -> None: self.assertFalse(convert_to_bool(False)) - def test_string_bool_none(self): + def test_string_bool_none(self) -> None: self.assertFalse(convert_to_bool(None)) - def test_string_bool_empty(self): + def test_string_bool_empty(self) -> None: self.assertFalse(convert_to_bool("")) - def test_string_bool_whitespace_only(self): + def test_string_bool_whitespace_only(self) -> None: self.assertFalse(convert_to_bool(" ")) - def test_non_boolean_string_raises_error(self): + def test_non_boolean_string_raises_error(self) -> None: with self.assertRaises(ValueError): convert_to_bool("garbage") - def test_non_boolean_number_raises_error(self): + def test_non_boolean_number_raises_error(self) -> None: with self.assertRaises(ValueError): convert_to_bool("200") @@ -68,10 +68,10 @@ def test_non_boolean_number_raises_error(self): class TiledMapTest(unittest.TestCase): filename = "tests/resources/test01.tmx" - def setUp(self): + def setUp(self) -> None: self.m = pytmx.TiledMap(self.filename) - def test_build_rects(self): + def test_build_rects(self) -> None: try: from pytmx import util_pygame @@ -82,30 +82,30 @@ def test_build_rects(self): except ImportError: pass - def test_get_tile_image(self): + def test_get_tile_image(self) -> None: image = self.m.get_tile_image(0, 0, 0) - def test_get_tile_image_by_gid(self): + def test_get_tile_image_by_gid(self) -> None: image = self.m.get_tile_image_by_gid(0) self.assertIsNone(image) image = self.m.get_tile_image_by_gid(1) self.assertIsNotNone(image) - def test_reserved_names_check_disabled_with_option(self): + def test_reserved_names_check_disabled_with_option(self) -> None: pytmx.TiledElement.allow_duplicate_names = False pytmx.TiledMap(allow_duplicate_names=True) self.assertTrue(pytmx.TiledElement.allow_duplicate_names) - def test_map_width_height_is_int(self): + def test_map_width_height_is_int(self) -> None: self.assertIsInstance(self.m.width, int) self.assertIsInstance(self.m.height, int) - def test_layer_width_height_is_int(self): + def test_layer_width_height_is_int(self) -> None: self.assertIsInstance(self.m.layers[0].width, int) self.assertIsInstance(self.m.layers[0].height, int) - def test_properties_are_converted_to_builtin_types(self): + def test_properties_are_converted_to_builtin_types(self) -> None: self.assertIsInstance(self.m.properties["test_bool"], bool) self.assertIsInstance(self.m.properties["test_color"], str) self.assertIsInstance(self.m.properties["test_file"], str) @@ -113,11 +113,11 @@ def test_properties_are_converted_to_builtin_types(self): self.assertIsInstance(self.m.properties["test_int"], int) self.assertIsInstance(self.m.properties["test_string"], str) - def test_properties_are_converted_to_correct_values(self): + def test_properties_are_converted_to_correct_values(self) -> None: self.assertFalse(self.m.properties["test_bool"]) self.assertTrue(self.m.properties["test_bool_true"]) - def test_pixels_to_tile_pos(self): + def test_pixels_to_tile_pos(self) -> None: self.assertEqual(self.m.pixels_to_tile_pos((0, 33)), (0, 2)) self.assertEqual(self.m.pixels_to_tile_pos((33, 0)), (2, 0)) self.assertEqual(self.m.pixels_to_tile_pos((0, 0)), (0, 0)) @@ -125,14 +125,14 @@ def test_pixels_to_tile_pos(self): class TiledElementTestCase(unittest.TestCase): - def setUp(self): + def setUp(self) -> None: self.element = TiledElement() - def test_from_xml_string_should_raise_on_TiledElement(self): + def test_from_xml_string_should_raise_on_TiledElement(self) -> None: with self.assertRaises(AttributeError): TiledElement.from_xml_string("") - def test_contains_reserved_property_name(self): + def test_contains_reserved_property_name(self) -> None: """Reserved names are checked from any attributes in the instance after it is created. Instance attributes are defaults from the specification. We check that new properties are not named same @@ -143,7 +143,7 @@ def test_contains_reserved_property_name(self): result = self.element._contains_invalid_property_name(items.items()) self.assertTrue(result) - def test_not_contains_reserved_property_name(self): + def test_not_contains_reserved_property_name(self) -> None: """Reserved names are checked from any attributes in the instance after it is created. Instance attributes are defaults from the specification. We check that new properties are not named same @@ -153,7 +153,7 @@ def test_not_contains_reserved_property_name(self): result = self.element._contains_invalid_property_name(items.items()) self.assertFalse(result) - def test_reserved_names_check_disabled_with_option(self): + def test_reserved_names_check_disabled_with_option(self) -> None: """Reserved names are checked from any attributes in the instance after it is created. Instance attributes are defaults from the specification. We check that new properties are not named same @@ -167,6 +167,6 @@ def test_reserved_names_check_disabled_with_option(self): result = self.element._contains_invalid_property_name(items.items()) self.assertFalse(result) - def test_repr(self): + def test_repr(self) -> None: self.element.name = "foo" self.assertEqual('', self.element.__repr__())