diff --git a/arcade/__init__.py b/arcade/__init__.py index 97b18275fa..a5152680d6 100644 --- a/arcade/__init__.py +++ b/arcade/__init__.py @@ -12,8 +12,8 @@ from pathlib import Path -if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] < 9): - sys.exit("The Arcade Library requires Python 3.9 or higher.") +if sys.version_info[0] < 3 or (sys.version_info[0] == 3 and sys.version_info[1] < 10): + sys.exit("The Arcade Library requires Python 3.10 or higher.") def configure_logging(level: int | None = None): diff --git a/arcade/application.py b/arcade/application.py index 7b7e17297d..763661bd7d 100644 --- a/arcade/application.py +++ b/arcade/application.py @@ -8,7 +8,8 @@ import logging import os import time -from typing import TYPE_CHECKING, Sequence +from collections.abc import Sequence +from typing import TYPE_CHECKING import pyglet import pyglet.gl as gl @@ -36,7 +37,7 @@ MOUSE_BUTTON_MIDDLE = 2 MOUSE_BUTTON_RIGHT = 4 -_window: "Window" +_window: Window __all__ = [ "get_screens", diff --git a/arcade/cache/hit_box.py b/arcade/cache/hit_box.py index da2e3d6096..c6b59637b6 100644 --- a/arcade/cache/hit_box.py +++ b/arcade/cache/hit_box.py @@ -118,7 +118,7 @@ def load(self, path: str | Path) -> None: with gzip.open(path, mode="rb") as fd: data = json.loads(fd.read()) else: - with open(path, mode="r") as fd: + with open(path) as fd: data = json.loads(fd.read()) for key, value in data.items(): diff --git a/arcade/cache/image_data.py b/arcade/cache/image_data.py index 0929728ba1..ac5991dfd7 100644 --- a/arcade/cache/image_data.py +++ b/arcade/cache/image_data.py @@ -23,9 +23,9 @@ class ImageDataCache: """ def __init__(self): - self._entries: dict[str, "ImageData"] = {} + self._entries: dict[str, ImageData] = {} - def put(self, name: str, image: "ImageData"): + def put(self, name: str, image: ImageData): """ Add an image to the cache. diff --git a/arcade/camera/camera_2d.py b/arcade/camera/camera_2d.py index d0b048a9b4..aa6b4a25d9 100644 --- a/arcade/camera/camera_2d.py +++ b/arcade/camera/camera_2d.py @@ -1,8 +1,9 @@ from __future__ import annotations +from collections.abc import Generator from contextlib import contextmanager from math import atan2, cos, degrees, radians, sin -from typing import TYPE_CHECKING, Generator +from typing import TYPE_CHECKING from pyglet.math import Vec2, Vec3 from typing_extensions import Self @@ -223,12 +224,12 @@ def from_camera_data( left, right = projection_data.left, projection_data.right if projection_data.left == projection_data.right: raise ZeroProjectionDimension( - (f"projection width is 0 due to equal {left=}and {right=} values") + f"projection width is 0 due to equal {left=}and {right=} values" ) bottom, top = projection_data.bottom, projection_data.top if bottom == top: raise ZeroProjectionDimension( - (f"projection height is 0 due to equal {bottom=}and {top=}") + f"projection height is 0 due to equal {bottom=}and {top=}" ) near, far = projection_data.near, projection_data.far if near == far: @@ -583,7 +584,7 @@ def projection(self) -> Rect: def projection(self, value: Rect) -> None: # Unpack and validate if not value: - raise ZeroProjectionDimension((f"Projection area is 0, {value.lrbt}")) + raise ZeroProjectionDimension(f"Projection area is 0, {value.lrbt}") _z = self._camera_data.zoom diff --git a/arcade/camera/data_types.py b/arcade/camera/data_types.py index 50c6704d6d..15b32a29b7 100644 --- a/arcade/camera/data_types.py +++ b/arcade/camera/data_types.py @@ -4,8 +4,9 @@ wide usage throughout Arcade's camera code. """ +from collections.abc import Generator from contextlib import contextmanager -from typing import Final, Generator, Protocol +from typing import Final, Protocol from pyglet.math import Vec2, Vec3 from typing_extensions import Self diff --git a/arcade/camera/default.py b/arcade/camera/default.py index cba9602fc1..dcb94d4b9f 100644 --- a/arcade/camera/default.py +++ b/arcade/camera/default.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Generator from contextlib import contextmanager -from typing import TYPE_CHECKING, Generator +from typing import TYPE_CHECKING from pyglet.math import Mat4, Vec2, Vec3 from typing_extensions import Self diff --git a/arcade/camera/orthographic.py b/arcade/camera/orthographic.py index 78061cae92..76b0ab10af 100644 --- a/arcade/camera/orthographic.py +++ b/arcade/camera/orthographic.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Generator from contextlib import contextmanager -from typing import TYPE_CHECKING, Generator +from typing import TYPE_CHECKING from pyglet.math import Mat4, Vec2, Vec3 from typing_extensions import Self diff --git a/arcade/camera/perspective.py b/arcade/camera/perspective.py index 0e1b26a83f..89ca15a0ed 100644 --- a/arcade/camera/perspective.py +++ b/arcade/camera/perspective.py @@ -1,8 +1,9 @@ from __future__ import annotations +from collections.abc import Generator from contextlib import contextmanager from math import radians, tan -from typing import TYPE_CHECKING, Generator +from typing import TYPE_CHECKING from pyglet.math import Mat4, Vec2, Vec3 from typing_extensions import Self diff --git a/arcade/camera/static.py b/arcade/camera/static.py index dff4151219..8bbca56e3c 100644 --- a/arcade/camera/static.py +++ b/arcade/camera/static.py @@ -1,7 +1,8 @@ from __future__ import annotations +from collections.abc import Callable, Generator from contextlib import contextmanager -from typing import TYPE_CHECKING, Callable, Generator +from typing import TYPE_CHECKING from pyglet.math import Mat4, Vec2, Vec3 diff --git a/arcade/context.py b/arcade/context.py index 56164afc4d..42ff53496d 100644 --- a/arcade/context.py +++ b/arcade/context.py @@ -3,8 +3,9 @@ Contains pre-loaded programs """ +from collections.abc import Iterable, Sequence from pathlib import Path -from typing import Any, Iterable, Sequence +from typing import Any import pyglet from PIL import Image diff --git a/arcade/easing.py b/arcade/easing.py index a88dfb4900..aa2a2ddf5b 100644 --- a/arcade/easing.py +++ b/arcade/easing.py @@ -2,9 +2,9 @@ Functions used to support easing """ +from collections.abc import Callable from dataclasses import dataclass from math import cos, pi, sin -from typing import Callable from .math import get_distance diff --git a/arcade/examples/dual_stick_shooter.py b/arcade/examples/dual_stick_shooter.py index aba4ca5390..d8f7711efe 100644 --- a/arcade/examples/dual_stick_shooter.py +++ b/arcade/examples/dual_stick_shooter.py @@ -31,17 +31,17 @@ def dump_obj(obj): for key in sorted(vars(obj)): val = getattr(obj, key) - print("{:30} = {} ({})".format(key, val, type(val).__name__)) + print(f"{key:30} = {val} ({type(val).__name__})") def dump_controller(controller): - print("========== {}".format(controller)) - print("Left X {}".format(controller.leftx)) - print("Left Y {}".format(controller.lefty)) - print("Left Trigger {}".format(controller.lefttrigger)) - print("Right X {}".format(controller.rightx)) - print("Right Y {}".format(controller.righty)) - print("Right Trigger {}".format(controller.righttrigger)) + print(f"========== {controller}") + print(f"Left X {controller.leftx}") + print(f"Left Y {controller.lefty}") + print(f"Left Trigger {controller.lefttrigger}") + print(f"Right X {controller.rightx}") + print(f"Right Y {controller.righty}") + print(f"Right Trigger {controller.righttrigger}") print("========== Extra controller") dump_obj(controller) print("========== Extra controller.device") diff --git a/arcade/examples/gl/3d_cube_with_cubes.py b/arcade/examples/gl/3d_cube_with_cubes.py index 4ef7082995..198c786321 100644 --- a/arcade/examples/gl/3d_cube_with_cubes.py +++ b/arcade/examples/gl/3d_cube_with_cubes.py @@ -97,11 +97,11 @@ def __init__(self, width, height, title): self.frame = 0 self.fbo1 = self.ctx.framebuffer( - color_attachments=[self.ctx.texture((self.get_size()))], + color_attachments=[self.ctx.texture(self.get_size())], depth_attachment=self.ctx.depth_texture(self.get_size()), ) self.fbo2 = self.ctx.framebuffer( - color_attachments=[self.ctx.texture((self.get_size()))], + color_attachments=[self.ctx.texture(self.get_size())], depth_attachment=self.ctx.depth_texture(self.get_size()), ) diff --git a/arcade/examples/gl/bindless_texture.py b/arcade/examples/gl/bindless_texture.py index b6e8db146e..c86a7d3348 100644 --- a/arcade/examples/gl/bindless_texture.py +++ b/arcade/examples/gl/bindless_texture.py @@ -26,7 +26,6 @@ """ from array import array -from typing import List from itertools import cycle import arcade @@ -144,7 +143,7 @@ def __init__(self): ) self.handles = [] - self.textures: List[Texture2D] = [] + self.textures: list[Texture2D] = [] # Make a cycle iterator from Arcade's resources (images) resources = arcade.resources.list_built_in_assets(name="female", extensions=(".png",)) resource_cycle = cycle(resources) diff --git a/arcade/examples/gl/render_indirect.py b/arcade/examples/gl/render_indirect.py index 6af50d1b7b..234f70a46c 100644 --- a/arcade/examples/gl/render_indirect.py +++ b/arcade/examples/gl/render_indirect.py @@ -238,12 +238,12 @@ def on_draw(self): # to prove that partial rendering also works. # NOTE: These values can be skewed if vsync is enabled print( - ( + f"[{method}] " f"Pixels written = {self.query.samples_passed // 4}, " f"Primitives Generated = {self.query.primitives_generated}, " f"time = {self.query.time_elapsed / 1_000_000_000}s" - ) + ) def on_update(self, delta_time: float): diff --git a/arcade/examples/gl/spritelist_interaction_visualize_dist_los_trans.py b/arcade/examples/gl/spritelist_interaction_visualize_dist_los_trans.py index 6653ca58a9..0dd505ef01 100644 --- a/arcade/examples/gl/spritelist_interaction_visualize_dist_los_trans.py +++ b/arcade/examples/gl/spritelist_interaction_visualize_dist_los_trans.py @@ -202,11 +202,11 @@ def on_draw(self): ) print("Indices found:", sprite_indices) print( - ( + f"max(sprite_indices) = {max(sprite_indices)} | " f"len(self.coins) = {len(self.coins)} | " f"sprite_indices = {len(sprite_indices)}" - ) + ) # Resolve the list of selected sprites and remove them sprites = [self.coins[int(i)] for i in sprite_indices] diff --git a/arcade/examples/gui/exp_controller_inventory.py b/arcade/examples/gui/exp_controller_inventory.py index 11a9724504..ffe99f02b0 100644 --- a/arcade/examples/gui/exp_controller_inventory.py +++ b/arcade/examples/gui/exp_controller_inventory.py @@ -15,7 +15,6 @@ """ # TODO: Drag and Drop -from typing import List, Optional import pyglet.font from pyglet.event import EVENT_HANDLED @@ -75,7 +74,7 @@ class Inventory: """ def __init__(self, capacity: int): - self._items: List[Item | None] = [None for _ in range(capacity)] + self._items: list[Item | None] = [None for _ in range(capacity)] self.capacity = capacity def add(self, item: Item): @@ -352,7 +351,7 @@ def __init__(self, inventory: Inventory, **kwargs): # init controller support self.detect_focusable_widgets() - def on_event(self, event: UIEvent) -> Optional[bool]: + def on_event(self, event: UIEvent) -> bool | None: if isinstance(event, UIControllerButtonPressEvent): if event.button == "b": self.set_focus(self.close_button) diff --git a/arcade/examples/gui/exp_controller_support.py b/arcade/examples/gui/exp_controller_support.py index f85b207fbd..40dd979722 100644 --- a/arcade/examples/gui/exp_controller_support.py +++ b/arcade/examples/gui/exp_controller_support.py @@ -9,7 +9,6 @@ python -m arcade.examples.gui.exp_controller_support """ -from typing import Optional import arcade from arcade import Texture @@ -115,7 +114,7 @@ def input_prompts(cls, event: UIControllerEvent) -> Texture | None: return None - def on_event(self, event: UIEvent) -> Optional[bool]: + def on_event(self, event: UIEvent) -> bool | None: if isinstance(event, UIControllerEvent): input_texture = self.input_prompts(event) diff --git a/arcade/examples/gui/exp_controller_support_grid.py b/arcade/examples/gui/exp_controller_support_grid.py index 4833b0fcab..51f1dd0772 100644 --- a/arcade/examples/gui/exp_controller_support_grid.py +++ b/arcade/examples/gui/exp_controller_support_grid.py @@ -9,7 +9,6 @@ python -m arcade.examples.gui.exp_controller_support_grid """ -from typing import Dict, Tuple import arcade from arcade.examples.gui.exp_controller_support import ControllerIndicator @@ -27,7 +26,7 @@ class FocusableButton(UIFocusable, UIFlatButton): pass -def setup_grid_focus_transition(grid: Dict[Tuple[int, int], UIWidget]): +def setup_grid_focus_transition(grid: dict[tuple[int, int], UIWidget]): """Setup focus transition in grid. Connect focus transition between `Focusable` in grid. diff --git a/arcade/examples/particle_systems.py b/arcade/examples/particle_systems.py index 6ba6fcf1cf..c39df62124 100644 --- a/arcade/examples/particle_systems.py +++ b/arcade/examples/particle_systems.py @@ -808,7 +808,7 @@ def __init__(self): def next_emitter(self, _time_delta): self.emitter_factory_id = (self.emitter_factory_id + 1) % len(self.factories) - print("Changing emitter to {}".format(self.emitter_factory_id)) + print(f"Changing emitter to {self.emitter_factory_id}") self.emitter_timeout = 0 self.label, self.emitter = self.factories[self.emitter_factory_id]() @@ -827,7 +827,7 @@ def on_draw(self): self.clear() arcade.draw_sprite(self.obj) if self.label: - arcade.draw_text("#{} {}".format(self.emitter_factory_id, self.label), + arcade.draw_text(f"#{self.emitter_factory_id} {self.label}", WINDOW_WIDTH / 2, WINDOW_HEIGHT - 25, arcade.color.PALE_GOLD, 20, width=WINDOW_WIDTH, anchor_x="center") diff --git a/arcade/examples/sprite_health.py b/arcade/examples/sprite_health.py index 636cc45c6e..f9947b5bbd 100644 --- a/arcade/examples/sprite_health.py +++ b/arcade/examples/sprite_health.py @@ -7,7 +7,6 @@ python -m arcade.examples.sprite_health """ import math -from typing import Tuple import arcade from arcade.types import Color @@ -102,13 +101,13 @@ def __init__( self, owner: Player, sprite_list: arcade.SpriteList, - position: Tuple[float, float] = (0, 0), + position: tuple[float, float] = (0, 0), full_color: Color = arcade.color.GREEN, background_color: Color = arcade.color.BLACK, width: int = 100, height: int = 4, border_size: int = 4, - scale: Tuple[float, float] = (1.0, 1.0), + scale: tuple[float, float] = (1.0, 1.0), ) -> None: # Store the reference to the owner and the sprite list self.owner: Player = owner @@ -120,7 +119,7 @@ def __init__( self._center_x: float = 0.0 self._center_y: float = 0.0 self._fullness: float = 0.0 - self._scale: Tuple[float, float] = (1.0, 1.0) + self._scale: tuple[float, float] = (1.0, 1.0) # Create the boxes needed to represent the indicator bar self._background_box: arcade.SpriteSolidColor = arcade.SpriteSolidColor( @@ -220,12 +219,12 @@ def fullness(self, new_fullness: float) -> None: self.full_box.left = self._center_x - (self._bar_width / 2) * self.scale[0] @property - def position(self) -> Tuple[float, float]: + def position(self) -> tuple[float, float]: """Returns the current position of the bar.""" return self._center_x, self._center_y @position.setter - def position(self, new_position: Tuple[float, float]) -> None: + def position(self, new_position: tuple[float, float]) -> None: """Sets the new position of the bar.""" # Check if the position has changed. If so, change the bar's position if new_position != self.position: @@ -237,12 +236,12 @@ def position(self, new_position: Tuple[float, float]) -> None: self.full_box.left = self._center_x - (self._bar_width / 2) * self.scale[0] @property - def scale(self) -> Tuple[float, float]: + def scale(self) -> tuple[float, float]: """Returns the scale of the bar.""" return self._scale @scale.setter - def scale(self, value: Tuple[float, float]) -> None: + def scale(self, value: tuple[float, float]) -> None: """Sets the new scale of the bar.""" # Check if the scale has changed. If so, change the bar's scale if value != self.scale: diff --git a/arcade/exceptions.py b/arcade/exceptions.py index 945d07e07a..a3b651823f 100644 --- a/arcade/exceptions.py +++ b/arcade/exceptions.py @@ -4,7 +4,7 @@ import functools import warnings -from typing import Type, TypeVar +from typing import TypeVar __all__ = [ "OutsideRangeError", @@ -19,7 +19,7 @@ # Since this module forbids importing from the rest of # Arcade, we make our own local type variables. -_TType = TypeVar("_TType", bound=Type) +_TType = TypeVar("_TType", bound=type) _CT = TypeVar("_CT") # Comparable type, ie supports the <= operator diff --git a/arcade/experimental/shadertoy.py b/arcade/experimental/shadertoy.py index ca13c53b86..41158e7279 100644 --- a/arcade/experimental/shadertoy.py +++ b/arcade/experimental/shadertoy.py @@ -430,7 +430,7 @@ def resize(self, size: tuple[int, int]): return self._size = size # Resize the internal texture and fbo + clear - self._texture.resize((self._size)) + self._texture.resize(self._size) self._fbo.resize() self._fbo.clear() diff --git a/arcade/experimental/shapes_perf.py b/arcade/experimental/shapes_perf.py index b4dc8f5c65..51887961d4 100644 --- a/arcade/experimental/shapes_perf.py +++ b/arcade/experimental/shapes_perf.py @@ -212,11 +212,9 @@ def on_draw(self): if self.execution_time > 1.0 and self.frames > 0: print( - ( - f"frames {self.frames}, " - f"execution time {round(self.execution_time, 3)}, " - f"frame time {round(self.execution_time / self.frames, 3)}" - ) + f"frames {self.frames}, " + f"execution time {round(self.execution_time, 3)}, " + f"frame time {round(self.execution_time / self.frames, 3)}" ) self.execution_time = 0 self.frames = 0 diff --git a/arcade/future/background/__init__.py b/arcade/future/background/__init__.py index 22e701ba75..53e8806a1e 100644 --- a/arcade/future/background/__init__.py +++ b/arcade/future/background/__init__.py @@ -1,5 +1,3 @@ -from typing import Tuple - from PIL import Image import arcade.gl as gl @@ -22,7 +20,7 @@ def texture_from_file( tex_src: str, - offset: Tuple[float, float] = (0.0, 0.0), + offset: tuple[float, float] = (0.0, 0.0), scale: float = 1.0, angle: float = 0.0, filters=(gl.NEAREST, gl.NEAREST), @@ -41,15 +39,15 @@ def texture_from_file( def background_from_file( tex_src: str, - pos: Tuple[float, float] = (0.0, 0.0), - size: Tuple[int, int] | None = None, - offset: Tuple[float, float] = (0.0, 0.0), + pos: tuple[float, float] = (0.0, 0.0), + size: tuple[int, int] | None = None, + offset: tuple[float, float] = (0.0, 0.0), scale: float = 1.0, angle: float = 0.0, *, filters=(gl.NEAREST, gl.NEAREST), - color: Tuple[int, int, int] | None = None, - color_norm: Tuple[float, float, float] | None = None, + color: tuple[int, int, int] | None = None, + color_norm: tuple[float, float, float] | None = None, shader: gl.Program | None = None, geometry: gl.Geometry | None = None, ) -> Background: diff --git a/arcade/future/background/groups.py b/arcade/future/background/groups.py index 081bd09a83..e1722441c2 100644 --- a/arcade/future/background/groups.py +++ b/arcade/future/background/groups.py @@ -135,7 +135,7 @@ def __getitem__(self, item: int): return self._backgrounds[item], self._depths[item] def __setitem__(self, key: int, value: Background | float): - if isinstance(value, (float, int)): + if isinstance(value, float | int): self._depths[key] = value else: self._backgrounds[key] = value diff --git a/arcade/future/input/input_manager_example.py b/arcade/future/input/input_manager_example.py index 284c31c87c..56bc942ee7 100644 --- a/arcade/future/input/input_manager_example.py +++ b/arcade/future/input/input_manager_example.py @@ -1,6 +1,6 @@ # type: ignore import random -from typing import Sequence +from collections.abc import Sequence import pyglet from pyglet.input import Controller diff --git a/arcade/future/input/input_mapping.py b/arcade/future/input/input_mapping.py index a6acd962d5..3198db7401 100644 --- a/arcade/future/input/input_mapping.py +++ b/arcade/future/input/input_mapping.py @@ -42,7 +42,7 @@ def __init__(self, input: inputs.InputEnum): except KeyError: raise TypeError( f"Got {input} input specified for ActionMapping must be of of: " - f"{', '.join((t.__name__ for t in inputs.CLASS_TO_INPUT_TYPE.keys()))}" + f"{', '.join(t.__name__ for t in inputs.CLASS_TO_INPUT_TYPE.keys())}" ) self._input = input diff --git a/arcade/future/input/inputs.py b/arcade/future/input/inputs.py index add46e8237..1371e750bb 100644 --- a/arcade/future/input/inputs.py +++ b/arcade/future/input/inputs.py @@ -7,7 +7,6 @@ from enum import Enum, auto from sys import platform -from typing import Type from arcade.future.input.raw_dicts import RawBindBase @@ -28,7 +27,7 @@ class InputEnum(Enum): class StrEnum(str, InputEnum): def __new__(cls, value, *args, **kwargs): - if not isinstance(value, (str, auto)): + if not isinstance(value, str | auto): raise TypeError(f"Values of StrEnums must be strings: {value!r} is a {type(value)}") return super().__new__(cls, value, *args, **kwargs) @@ -362,7 +361,7 @@ class MouseButtons(InputEnum): # 2. Types are hashable # It may be worth encapsulating this approach since we have other if # ladders without case-specific logic remaining in the controller code. -CLASS_TO_INPUT_TYPE: dict[Type[InputEnum], InputType] = { +CLASS_TO_INPUT_TYPE: dict[type[InputEnum], InputType] = { Keys: InputType.KEYBOARD, MouseButtons: InputType.MOUSE_BUTTON, MouseAxes: InputType.MOUSE_AXIS, @@ -370,7 +369,7 @@ class MouseButtons(InputEnum): ControllerAxes: InputType.CONTROLLER_AXIS, } -INPUT_TYPE_TO_CLASS: dict[InputType, Type[InputEnum]] = { +INPUT_TYPE_TO_CLASS: dict[InputType, type[InputEnum]] = { InputType.KEYBOARD: Keys, InputType.MOUSE_BUTTON: MouseButtons, InputType.MOUSE_AXIS: MouseAxes, diff --git a/arcade/future/input/manager.py b/arcade/future/input/manager.py index 104a6635d9..a530907be1 100644 --- a/arcade/future/input/manager.py +++ b/arcade/future/input/manager.py @@ -1,8 +1,9 @@ # type: ignore from __future__ import annotations +from collections.abc import Callable from enum import Enum -from typing import Any, Callable, TypeVar +from typing import Any, TypeVar import pyglet from pyglet.input.base import Controller diff --git a/arcade/future/input/raw_dicts.py b/arcade/future/input/raw_dicts.py index 7ec453982a..f767f2e12a 100644 --- a/arcade/future/input/raw_dicts.py +++ b/arcade/future/input/raw_dicts.py @@ -3,8 +3,6 @@ Placing them here prevents circular import issues. """ -from typing import Union - from typing_extensions import TypedDict @@ -23,7 +21,7 @@ class RawBindBase(TypedDict): """ input_type: int - input: Union[str, int] + input: str | int class RawActionMapping(RawBindBase): diff --git a/arcade/future/light/lights.py b/arcade/future/light/lights.py index 243cb51570..916af693c8 100644 --- a/arcade/future/light/lights.py +++ b/arcade/future/light/lights.py @@ -1,5 +1,5 @@ from array import array -from typing import Iterator, Sequence +from collections.abc import Iterator, Sequence from arcade import gl from arcade.color import WHITE diff --git a/arcade/future/sub_clock.py b/arcade/future/sub_clock.py index 339e324db0..ab32c39f66 100644 --- a/arcade/future/sub_clock.py +++ b/arcade/future/sub_clock.py @@ -1,7 +1,5 @@ from __future__ import annotations -from typing import Union - from arcade.clock import GLOBAL_CLOCK, Clock @@ -55,7 +53,7 @@ class SubClock(Clock): i.e. a value of 0.5 means time elapsed half as fast for this clock. Defaults to 1.0. """ - def __init__(self, parent: Union[Clock, SubClock, None] = None, tick_speed: float = 1) -> None: + def __init__(self, parent: Clock | SubClock | None = None, tick_speed: float = 1) -> None: parent = parent or GLOBAL_CLOCK super().__init__(parent._elapsed_time, parent._tick, tick_speed) self.children: list[SubClock] = [] diff --git a/arcade/gui/experimental/focus.py b/arcade/gui/experimental/focus.py index 204c8efad6..02c660c189 100644 --- a/arcade/gui/experimental/focus.py +++ b/arcade/gui/experimental/focus.py @@ -1,6 +1,5 @@ import warnings from types import EllipsisType -from typing import Optional from pyglet.event import EVENT_HANDLED, EVENT_UNHANDLED from pyglet.math import Vec2 @@ -76,7 +75,7 @@ def __init__(self, *args, **kwargs): bind(self, "_focused_widget", self.trigger_full_render) bind(self, "_focusable_widgets", self.trigger_full_render) - def on_event(self, event: UIEvent) -> Optional[bool]: + def on_event(self, event: UIEvent) -> bool | None: # pass events to children first, including controller events # so they can handle them if super().on_event(event): diff --git a/arcade/gui/experimental/scroll_area.py b/arcade/gui/experimental/scroll_area.py index 0b0a22e77f..fbe715cd0e 100644 --- a/arcade/gui/experimental/scroll_area.py +++ b/arcade/gui/experimental/scroll_area.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import Iterable, TypeVar +from collections.abc import Iterable +from typing import TypeVar from pyglet.event import EVENT_UNHANDLED diff --git a/arcade/gui/experimental/typed_text_input.py b/arcade/gui/experimental/typed_text_input.py index 348f14b395..e11c2b2a01 100644 --- a/arcade/gui/experimental/typed_text_input.py +++ b/arcade/gui/experimental/typed_text_input.py @@ -1,4 +1,5 @@ -from typing import Callable, Generic, Type, TypeVar, cast +from collections.abc import Callable +from typing import Generic, TypeVar, cast import arcade from arcade.color import BLACK, RED, WHITE @@ -80,7 +81,7 @@ class UITypedTextInput(UIInputText, Generic[T]): def __init__( self, - parsed_type: Type[T], + parsed_type: type[T], *, to_str: Callable[[T], str] = repr, from_str: Callable[[str], T] | None = None, @@ -121,7 +122,7 @@ def __init__( self.emit_parse_exceptions = emit_parse_exceptions self._error_color = error_color self._valid_color = text_color - self._parsed_type: Type[T] = parsed_type + self._parsed_type: type[T] = parsed_type self._to_str = to_str self._from_str: Callable[[str], T] = cast(Callable[[str], T], from_str or parsed_type) self._parsed_value: T = self._from_str(self.text) @@ -157,7 +158,7 @@ def on_event(self, event: UIEvent) -> bool | None: return handled @property - def parsed_type(self) -> Type[T]: + def parsed_type(self) -> type[T]: """Get the type this input field expects to parse. .. note:: This is not meant to be changed after creation. diff --git a/arcade/gui/property.py b/arcade/gui/property.py index 95e46ee244..c9332c3ad4 100644 --- a/arcade/gui/property.py +++ b/arcade/gui/property.py @@ -1,6 +1,7 @@ import sys import traceback -from typing import Any, Callable, Dict, Generic, List, Optional, TypeVar, cast +from collections.abc import Callable +from typing import Any, Generic, TypeVar, cast from weakref import WeakKeyDictionary, ref from typing_extensions import Self, overload, override @@ -59,8 +60,8 @@ class MyObject: def __init__( self, - default: Optional[P] = None, - default_factory: Optional[Callable[[Any, Any], P]] = None, + default: P | None = None, + default_factory: Callable[[Any, Any], P] | None = None, ): if default_factory is None: default_factory = lambda prop, instance: cast(P, default) @@ -276,7 +277,7 @@ def update(self, *args): V = TypeVar("V") -class DictProperty(Property[Dict[K, V]], Generic[K, V]): +class DictProperty(Property[dict[K, V]], Generic[K, V]): """Property that represents a dict. Only dict are allowed. Any other classes are forbidden. @@ -384,7 +385,7 @@ def reverse(self): self.dispatch() -class ListProperty(Property[List[P]], Generic[P]): +class ListProperty(Property[list[P]], Generic[P]): """Property that represents a list. Only list are allowed. Any other classes are forbidden. diff --git a/arcade/gui/surface.py b/arcade/gui/surface.py index 200ebce209..0ac859b7fd 100644 --- a/arcade/gui/surface.py +++ b/arcade/gui/surface.py @@ -1,6 +1,6 @@ from array import array +from collections.abc import Generator from contextlib import contextmanager -from typing import Generator from PIL import Image from pyglet.math import Vec2, Vec4 diff --git a/arcade/gui/ui_manager.py b/arcade/gui/ui_manager.py index 6d25be8bf7..59be4e19ad 100644 --- a/arcade/gui/ui_manager.py +++ b/arcade/gui/ui_manager.py @@ -9,12 +9,12 @@ """ from collections import defaultdict -from typing import Iterable, TypeVar, Union +from collections.abc import Iterable +from typing import TypeGuard, TypeVar from pyglet.event import EVENT_HANDLED, EVENT_UNHANDLED, EventDispatcher from pyglet.input import Controller from pyglet.math import Vec2 -from typing_extensions import TypeGuard import arcade from arcade.experimental.controller_window import ControllerWindow @@ -400,7 +400,7 @@ def adjust_mouse_coordinates(self, x: float, y: float) -> tuple[float, float]: x_, y_, *c = self.camera.unproject((x, y)) # convert screen to ui coordinates return x_, y_ - def on_event(self, event) -> Union[bool, None]: + def on_event(self, event) -> bool | None: """Forwards an event to all widgets in the UIManager.""" layers = sorted(self.children.keys(), reverse=True) for layer in layers: diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index e2b0e69565..244024cb37 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -1,8 +1,9 @@ from __future__ import annotations from abc import ABC +from collections.abc import Iterable from enum import IntEnum -from typing import TYPE_CHECKING, Dict, Iterable, List, NamedTuple, Optional, Tuple, TypeVar, Union +from typing import TYPE_CHECKING, NamedTuple, TypeVar from pyglet.event import EVENT_HANDLED, EVENT_UNHANDLED, EventDispatcher from pyglet.math import Vec2 @@ -45,8 +46,8 @@ class FocusMode(IntEnum): class _ChildEntry(NamedTuple): - child: "UIWidget" - data: Dict + child: UIWidget + data: dict @copy_dunders_unimplemented @@ -74,15 +75,15 @@ class UIWidget(EventDispatcher, ABC): focused = Property(False) focus_mode: FocusMode = FocusMode.NONE - size_hint = Property[Optional[Tuple[Optional[float], Optional[float]]]](None) - size_hint_min = Property[Optional[Tuple[Optional[float], Optional[float]]]](None) - size_hint_max = Property[Optional[Tuple[Optional[float], Optional[float]]]](None) + size_hint = Property[tuple[float | None, float | None] | None](None) + size_hint_min = Property[tuple[float | None, float | None] | None](None) + size_hint_max = Property[tuple[float | None, float | None] | None](None) _children = ListProperty[_ChildEntry]() _border_width = Property(0) - _border_color = Property[Optional[Color]](arcade.color.BLACK) - _bg_color = Property[Optional[Color]]() - _bg_tex = Property[Union[Texture, NinePatchTexture, None]]() + _border_color = Property[Color | None](arcade.color.BLACK) + _bg_color = Property[Color | None]() + _bg_tex = Property[Texture | NinePatchTexture | None]() _padding_top = Property(0) _padding_right = Property(0) _padding_bottom = Property(0) @@ -100,11 +101,11 @@ def __init__( y: float = 0, width: float = 100, height: float = 100, - children: Iterable["UIWidget"] = tuple(), + children: Iterable[UIWidget] = tuple(), # Properties which might be used by layouts - size_hint: Optional[Tuple[float | None, float | None]] = None, # in percentage - size_hint_min: Optional[Tuple[float | None, float | None]] = None, # in pixel - size_hint_max: Optional[Tuple[float | None, float | None]] = None, # in pixel + size_hint: tuple[float | None, float | None] | None = None, # in percentage + size_hint_min: tuple[float | None, float | None] | None = None, # in pixel + size_hint_max: tuple[float | None, float | None] | None = None, # in pixel **kwargs, ): self._requires_render = True @@ -165,7 +166,7 @@ def add(self, child: W, **kwargs) -> W: return child - def remove(self, child: "UIWidget") -> dict | None: + def remove(self, child: UIWidget) -> dict | None: """Removes a child from the UIManager which was directly added to it. This will not remove widgets which are added to a child of UIManager. @@ -207,7 +208,7 @@ def on_event(self, event: UIEvent) -> bool | None: return EVENT_UNHANDLED - def _walk_parents(self) -> Iterable[Union["UIWidget", "UIManager"]]: + def _walk_parents(self) -> Iterable[UIWidget | UIManager]: parent = self.parent while isinstance(parent, UIWidget): yield parent @@ -386,7 +387,7 @@ def center(self) -> Vec2: return self.rect.center @center.setter - def center(self, value: Tuple[int, int]): + def center(self, value: tuple[int, int]): self.rect = self.rect.align_center(value) @property @@ -410,7 +411,7 @@ def padding(self): ) @padding.setter - def padding(self, args: Union[int, Tuple[int, int], Tuple[int, int, int, int]]): + def padding(self, args: int | tuple[int, int] | tuple[int, int, int, int]): if isinstance(args, int): # self.padding = 10 -> 10, 10, 10, 10 args = (args, args, args, args) @@ -424,7 +425,7 @@ def padding(self, args: Union[int, Tuple[int, int], Tuple[int, int, int, int]]): self._padding_left = pl @property - def children(self) -> List["UIWidget"]: + def children(self) -> list[UIWidget]: """Provides all child widgets.""" return [child for child, data in self._children] @@ -484,8 +485,8 @@ def with_padding( def with_background( self, *, - color: Union[None, Color] = ..., # type: ignore - texture: Union[None, Texture, NinePatchTexture] = ..., # type: ignore + color: None | Color = ..., # type: ignore + texture: None | Texture | NinePatchTexture = ..., # type: ignore ) -> Self: """Set widgets background. @@ -511,7 +512,7 @@ def with_background( return self @property - def content_size(self) -> Tuple[float, float]: + def content_size(self) -> tuple[float, float]: """Returns the size of the content area, which is the size of the widget minus padding and border.""" return self.content_width, self.content_height @@ -811,7 +812,7 @@ class UILayout(UIWidget): """ @staticmethod - def min_size_of(child: UIWidget) -> Tuple[float, float]: + def min_size_of(child: UIWidget) -> tuple[float, float]: """Resolves the minimum size of a child. If it has a size_hint set for the axis, it will use size_hint_min if set, otherwise the actual size will be used. """ diff --git a/arcade/gui/widgets/buttons.py b/arcade/gui/widgets/buttons.py index 60136e5398..adc0919e74 100644 --- a/arcade/gui/widgets/buttons.py +++ b/arcade/gui/widgets/buttons.py @@ -1,7 +1,5 @@ from dataclasses import dataclass -from typing import Optional, Union - -from typing_extensions import TypeAlias +from typing import TypeAlias import arcade from arcade import Texture, color, uicolor @@ -56,7 +54,7 @@ class UITextureButton(UIInteractiveWidget, UIStyledWidget[UITextureButtonStyle], size_hint_max: max width and height in pixel """ - _textures = DictProperty[str, Union[Texture, NinePatchTexture]]() + _textures = DictProperty[str, Texture | NinePatchTexture]() UIStyle = UITextureButtonStyle @@ -80,14 +78,14 @@ def __init__( y: float = 0, width: float | None = None, height: float | None = None, - texture: Union[None, Texture, NinePatchTexture] = None, - texture_hovered: Union[None, Texture, NinePatchTexture] = None, - texture_pressed: Union[None, Texture, NinePatchTexture] = None, - texture_disabled: Union[None, Texture, NinePatchTexture] = None, + texture: None | Texture | NinePatchTexture = None, + texture_hovered: None | Texture | NinePatchTexture = None, + texture_pressed: None | Texture | NinePatchTexture = None, + texture_disabled: None | Texture | NinePatchTexture = None, text: str = "", multiline: bool = False, scale: float | None = None, - style: Optional[dict[str, UIStyleBase]] = None, + style: dict[str, UIStyleBase] | None = None, size_hint=None, size_hint_min=None, size_hint_max=None, diff --git a/arcade/gui/widgets/dropdown.py b/arcade/gui/widgets/dropdown.py index 27c2332911..afa75c495a 100644 --- a/arcade/gui/widgets/dropdown.py +++ b/arcade/gui/widgets/dropdown.py @@ -1,5 +1,4 @@ from copy import deepcopy -from typing import Optional, Union from pyglet.event import EVENT_HANDLED @@ -31,7 +30,7 @@ def hide(self): if self.parent: self.parent.remove(self) - def on_event(self, event: UIEvent) -> Optional[bool]: + def on_event(self, event: UIEvent) -> bool | None: if isinstance(event, UIMousePressEvent): # Click outside of dropdown options if not self.rect.point_in_rect((event.x, event.y)): @@ -119,7 +118,7 @@ def __init__( width: float = 150, height: float = 30, default: str | None = None, - options: Optional[list[Union[str, None]]] = None, + options: list[str | None] | None = None, primary_style=None, dropdown_style=None, active_style=None, diff --git a/arcade/gui/widgets/image.py b/arcade/gui/widgets/image.py index b4b4e90eab..f732753168 100644 --- a/arcade/gui/widgets/image.py +++ b/arcade/gui/widgets/image.py @@ -1,5 +1,4 @@ import math -from typing import Union from typing_extensions import override @@ -28,7 +27,7 @@ class UIImage(UIWidget): **kwargs: passed to UIWidget """ - texture = Property[Union[Texture, NinePatchTexture]]() + texture = Property[Texture | NinePatchTexture]() """Texture to show""" alpha = Property(255) """Alpha value of the texture, value between 0 and 255. @@ -40,7 +39,7 @@ class UIImage(UIWidget): def __init__( self, *, - texture: Union[Texture, NinePatchTexture], + texture: Texture | NinePatchTexture, width: float | None = None, height: float | None = None, angle: int = 0, diff --git a/arcade/gui/widgets/layout.py b/arcade/gui/widgets/layout.py index a3d7205f32..4a10d19d38 100644 --- a/arcade/gui/widgets/layout.py +++ b/arcade/gui/widgets/layout.py @@ -1,10 +1,11 @@ from __future__ import annotations import warnings +from collections.abc import Iterable from dataclasses import dataclass -from typing import Dict, Iterable, List, Tuple, TypeVar +from typing import Literal, TypeVar -from typing_extensions import Literal, override +from typing_extensions import override from arcade.gui.property import bind, unbind from arcade.gui.widgets import UILayout, UIWidget, _ChildEntry @@ -74,7 +75,7 @@ def __init__( y: float = 0, width: float = 1, height: float = 1, - children: Iterable["UIWidget"] = tuple(), + children: Iterable[UIWidget] = tuple(), size_hint=(1, 1), size_hint_min=None, size_hint_max=None, @@ -296,7 +297,7 @@ def add(self, child: W, **kwargs) -> W: return super().add(child, **kwargs) @override - def remove(self, child: "UIWidget"): + def remove(self, child: UIWidget): """Remove a child from the layout.""" # unsubscribe from child's changes unbind(child, "_children", self._trigger_size_hint_update) @@ -482,7 +483,7 @@ def __init__( row_count: int = 1, **kwargs, ): - super(UIGridLayout, self).__init__( + super().__init__( x=x, y=y, width=width, @@ -550,7 +551,7 @@ def add( **kwargs, ) - def remove(self, child: "UIWidget"): + def remove(self, child: UIWidget): """Remove a child from the layout.""" # unsubscribe from child's changes unbind(child, "_children", self._trigger_size_hint_update) @@ -677,7 +678,7 @@ def do_layout(self): for i in range(self.row_count): rows.append([]) - lookup: Dict[Tuple[int, int], _ChildEntry] = {} + lookup: dict[tuple[int, int], _ChildEntry] = {} for entry in self._children: col_num = entry.data["column"] row_num = entry.data["row"] @@ -868,7 +869,7 @@ def from_widget_height(widget: UIWidget) -> _C: return _C.from_widget(widget, "height") -def _box_orthogonal_algorithm(constraints: list[_C], container_size: float) -> List[float]: +def _box_orthogonal_algorithm(constraints: list[_C], container_size: float) -> list[float]: """Calculate the 1 dimensional size of each entry based on the hint value and the available space in the container. @@ -889,7 +890,7 @@ def _box_orthogonal_algorithm(constraints: list[_C], container_size: float) -> L return [c._final_size for c in constraints] -def _box_axis_algorithm(constraints: list[_C], container_size: float) -> List[float]: +def _box_axis_algorithm(constraints: list[_C], container_size: float) -> list[float]: """ The box algorithm calculates the 1 dimensional size of each entry based on the hint value and the available space in the container. diff --git a/arcade/gui/widgets/slider.py b/arcade/gui/widgets/slider.py index 9d7fccede6..b17847acd0 100644 --- a/arcade/gui/widgets/slider.py +++ b/arcade/gui/widgets/slider.py @@ -2,8 +2,8 @@ import warnings from abc import ABCMeta, abstractmethod +from collections.abc import Mapping from dataclasses import dataclass -from typing import Mapping, Union from pyglet.event import EVENT_HANDLED, EVENT_UNHANDLED from typing_extensions import override @@ -67,8 +67,8 @@ def __init__( size_hint=None, size_hint_min=None, size_hint_max=None, - style: Union[Mapping[str, UISliderStyle], None] = None, - step: Union[float, None] = None, + style: Mapping[str, UISliderStyle] | None = None, + step: float | None = None, **kwargs, ): super().__init__( @@ -378,8 +378,8 @@ def __init__( size_hint=None, size_hint_min=None, size_hint_max=None, - style: Union[dict[str, UISliderStyle], None] = None, - step: Union[float, None] = None, + style: dict[str, UISliderStyle] | None = None, + step: float | None = None, **kwargs, ): super().__init__( @@ -530,8 +530,8 @@ class UITextureSlider(UISlider): def __init__( self, - track_texture: Union[Texture, NinePatchTexture], - thumb_texture: Union[Texture, NinePatchTexture], + track_texture: Texture | NinePatchTexture, + thumb_texture: Texture | NinePatchTexture, style=None, **kwargs, ): diff --git a/arcade/gui/widgets/text.py b/arcade/gui/widgets/text.py index 12b9b237be..91353eeb43 100644 --- a/arcade/gui/widgets/text.py +++ b/arcade/gui/widgets/text.py @@ -1,13 +1,13 @@ import warnings from copy import deepcopy from dataclasses import dataclass -from typing import Union +from typing import Literal import pyglet from pyglet.event import EVENT_HANDLED, EVENT_UNHANDLED from pyglet.text.caret import Caret from pyglet.text.document import AbstractDocument -from typing_extensions import Literal, override +from typing_extensions import override import arcade from arcade import uicolor @@ -513,7 +513,7 @@ def __init__( size_hint=None, size_hint_min=None, size_hint_max=None, - style: Union[dict[str, UIInputTextStyle], None] = None, + style: dict[str, UIInputTextStyle] | None = None, **kwargs, ): if border_color != arcade.color.WHITE or border_width != 2: diff --git a/arcade/math.py b/arcade/math.py index bf9d03de60..a35237fcad 100644 --- a/arcade/math.py +++ b/arcade/math.py @@ -375,7 +375,7 @@ def rescale_relative_to_point(source: Point2, target: Point2, factor: AsFloat | The rescaled point. """ - if isinstance(factor, (float, int)): + if isinstance(factor, float | int): if factor == 1.0: return target scale_x = scale_y = factor diff --git a/arcade/particles/emitter.py b/arcade/particles/emitter.py index 6067472b1d..c5e7e7a8a1 100644 --- a/arcade/particles/emitter.py +++ b/arcade/particles/emitter.py @@ -5,7 +5,7 @@ from __future__ import annotations -from typing import Callable +from collections.abc import Callable import arcade from arcade import Vec2 @@ -135,7 +135,7 @@ def __init__( self, center_xy: Point, emit_controller: EmitController, - particle_factory: Callable[["Emitter"], Particle], + particle_factory: Callable[[Emitter], Particle], change_xy: Velocity = (0.0, 0.0), emit_done_cb: Callable[[Emitter], None] | None = None, reap_cb: Callable[[], None] | None = None, diff --git a/arcade/particles/emitter_simple.py b/arcade/particles/emitter_simple.py index e049932b23..04523a6e72 100644 --- a/arcade/particles/emitter_simple.py +++ b/arcade/particles/emitter_simple.py @@ -6,7 +6,7 @@ """ import random -from typing import Sequence +from collections.abc import Sequence from arcade.math import rand_in_circle, rand_on_circle from arcade.types import PathOrTexture, Point diff --git a/arcade/paths.py b/arcade/paths.py index 2990a801ea..a6cb3b249c 100644 --- a/arcade/paths.py +++ b/arcade/paths.py @@ -58,7 +58,7 @@ def _heuristic(start: Point2, goal: Point2) -> float: return d * (dx + dy) + (d2 - 2 * d) * min(dx, dy) -class _AStarGraph(object): +class _AStarGraph: """ A grid which tracks 2 barriers and a moving sprite. diff --git a/arcade/physics_engines.py b/arcade/physics_engines.py index ab8c1412dd..d68275d77e 100644 --- a/arcade/physics_engines.py +++ b/arcade/physics_engines.py @@ -3,7 +3,7 @@ """ import math -from typing import Iterable +from collections.abc import Iterable from arcade import ( BasicSprite, diff --git a/arcade/pymunk_physics_engine.py b/arcade/pymunk_physics_engine.py index da5199a2a4..7e966d5724 100644 --- a/arcade/pymunk_physics_engine.py +++ b/arcade/pymunk_physics_engine.py @@ -4,7 +4,7 @@ import logging import math -from typing import Callable +from collections.abc import Callable import pymunk from pyglet.math import Vec2 diff --git a/arcade/resources/__init__.py b/arcade/resources/__init__.py index d14aff5229..b506e338c6 100644 --- a/arcade/resources/__init__.py +++ b/arcade/resources/__init__.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Sequence +from collections.abc import Sequence from arcade.exceptions import warning, ReplacementWarning #: The absolute path to this directory @@ -93,11 +93,9 @@ def resolve(path: str | Path) -> Path: else: searched_paths = "\n".join(f"-> {p}" for p in reversed(paths)) raise FileNotFoundError( - ( - f"Cannot locate resource '{resource}' using handle " - f"'{handle}' in any of the following paths:\n" - f"{searched_paths}" - ) + f"Cannot locate resource '{resource}' using handle " + f"'{handle}' in any of the following paths:\n" + f"{searched_paths}" ) # Always convert into a Path object diff --git a/arcade/scene.py b/arcade/scene.py index dac305ec7b..0d9a3fb060 100644 --- a/arcade/scene.py +++ b/arcade/scene.py @@ -10,7 +10,7 @@ * Control sprite list draw order within the group """ -from typing import Iterable +from collections.abc import Iterable from warnings import warn from arcade import Sprite, SpriteList @@ -404,7 +404,7 @@ def update( **kwargs: Additional keyword arguments propagated down to sprites """ # Due to api changes in 3.0 we sanity check delta_time - if not isinstance(delta_time, (int, float)): + if not isinstance(delta_time, int | float): raise TypeError( f"Expected a number for delta_time, but got {type(delta_time)} instead." ) diff --git a/arcade/sections.py b/arcade/sections.py index 4b34ab10b8..7426a1a4f1 100644 --- a/arcade/sections.py +++ b/arcade/sections.py @@ -1,7 +1,8 @@ from __future__ import annotations import math -from typing import TYPE_CHECKING, Generator, Iterable +from collections.abc import Generator, Iterable +from typing import TYPE_CHECKING from pyglet.event import EVENT_HANDLED, EVENT_UNHANDLED @@ -264,7 +265,7 @@ def window(self): else: return self._view.window - def overlaps_with(self, section: "Section") -> bool: + def overlaps_with(self, section: Section) -> bool: """Checks if this section overlaps with another section Args: diff --git a/arcade/shape_list.py b/arcade/shape_list.py index af01cde14b..08d1437531 100644 --- a/arcade/shape_list.py +++ b/arcade/shape_list.py @@ -10,10 +10,9 @@ import math from array import array from collections import OrderedDict +from collections.abc import Iterable, Sequence from typing import ( Generic, - Iterable, - Sequence, TypeVar, cast, ) diff --git a/arcade/sound.py b/arcade/sound.py index 292a6e08ba..5eac3d2cdd 100644 --- a/arcade/sound.py +++ b/arcade/sound.py @@ -311,7 +311,7 @@ def play_sound( elif not isinstance(sound, Sound): raise TypeError( f"Error, got {sound!r} instead of an arcade.Sound." - if not isinstance(sound, (str, Path, bytes)) + if not isinstance(sound, str | Path | bytes) else " Make sure to use load_sound first, then play the result with play_sound." ) diff --git a/arcade/sprite/base.py b/arcade/sprite/base.py index 2a785e0366..8ecec9657e 100644 --- a/arcade/sprite/base.py +++ b/arcade/sprite/base.py @@ -1,6 +1,7 @@ from __future__ import annotations -from typing import TYPE_CHECKING, Any, Iterable, TypeVar +from collections.abc import Iterable +from typing import TYPE_CHECKING, Any, TypeVar import arcade from arcade.color import BLACK, WHITE @@ -68,7 +69,7 @@ def __init__( self._depth = 0.0 self._texture = texture width, height = texture.size - self._scale = (scale, scale) if isinstance(scale, (float, int)) else (scale[0], scale[1]) + self._scale = (scale, scale) if isinstance(scale, float | int) else (scale[0], scale[1]) # noqa: UP038 self._width = width * self._scale[0] self._height = height * self._scale[1] self._visible = bool(visible) @@ -81,7 +82,7 @@ def __init__( # All changes to this list should go through the pair of methods # register_sprite_list, _unregister_sprite_list. # They ensure that the above typing invariant is preserved. - self.sprite_lists: list["SpriteList[Any]"] = [] + self.sprite_lists: list[SpriteList[Any]] = [] """The sprite lists this sprite is a member of""" # Core properties we don't use, but spritelist expects it @@ -292,7 +293,7 @@ def scale(self) -> Point2: @scale.setter def scale(self, new_scale: Point2 | AsFloat): - if isinstance(new_scale, (float, int)): + if isinstance(new_scale, float | int): scale_x = new_scale scale_y = new_scale @@ -456,10 +457,8 @@ def rgb(self, color: RGBOrA255): except ValueError: # It's always a length issue raise ValueError( - ( - f"{self.__class__.__name__},rgb takes 3 or 4 channel" - f" colors, but got {len(color)} channels" - ) + f"{self.__class__.__name__},rgb takes 3 or 4 channel" + f" colors, but got {len(color)} channels" ) # Unpack to avoid index / . overhead & prep for repack @@ -661,7 +660,7 @@ def rescale_relative_to_point(self, point: Point2, scale_by: AsFloat | Point2) - """ # abort if the multiplier wouldn't do anything - if isinstance(scale_by, (float, int)): + if isinstance(scale_by, float | int): if scale_by == 1.0: return factor_x = scale_by diff --git a/arcade/sprite/sprite.py b/arcade/sprite/sprite.py index e67cf5795e..7f54dff850 100644 --- a/arcade/sprite/sprite.py +++ b/arcade/sprite/sprite.py @@ -73,7 +73,7 @@ def __init__( if isinstance(path_or_texture, Texture): _texture = path_or_texture _textures = [_texture] - elif isinstance(path_or_texture, (str, Path)): + elif isinstance(path_or_texture, str | Path): _texture = arcade.texture.default_texture_cache.load_or_get_texture(path_or_texture) _textures = [_texture] else: diff --git a/arcade/sprite_list/collision.py b/arcade/sprite_list/collision.py index 4d58f65f46..60330bb1eb 100644 --- a/arcade/sprite_list/collision.py +++ b/arcade/sprite_list/collision.py @@ -1,9 +1,5 @@ import struct -from typing import ( - Iterable, - List, - Tuple, -) +from collections.abc import Iterable from arcade import ( get_window, @@ -33,7 +29,7 @@ def get_distance_between_sprites(sprite1: SpriteType, sprite2: SpriteType) -> fl def get_closest_sprite( sprite: BasicSprite, sprite_list: SpriteSequence[SpriteType] -) -> Tuple[SpriteType, float] | None: +) -> tuple[SpriteType, float] | None: """ Given a Sprite and SpriteList, returns the closest sprite, and its distance. @@ -134,7 +130,7 @@ def _check_for_collision(sprite1: BasicSprite, sprite2: BasicSprite) -> bool: def _get_nearby_sprites( sprite: BasicSprite, sprite_list: SpriteSequence[SpriteType] -) -> List[SpriteType]: +) -> list[SpriteType]: sprite_count = len(sprite_list) if sprite_count == 0: return [] @@ -188,7 +184,7 @@ def check_for_collision_with_list( sprite: BasicSprite, sprite_list: SpriteSequence[SpriteType], method: int = 0, -) -> List[SpriteType]: +) -> list[SpriteType]: """ Check for a collision between a sprite, and a list of sprites. @@ -248,7 +244,7 @@ def check_for_collision_with_lists( sprite: BasicSprite, sprite_lists: Iterable[SpriteSequence[SpriteType]], method=1, -) -> List[SpriteType]: +) -> list[SpriteType]: """ Check for a collision between a Sprite, and a list of SpriteLists. @@ -271,7 +267,7 @@ def check_for_collision_with_lists( f"it is an instance of {type(sprite)}." ) - sprites: List[SpriteType] = [] + sprites: list[SpriteType] = [] sprites_to_check: Iterable[SpriteType] for sprite_list in sprite_lists: @@ -290,7 +286,7 @@ def check_for_collision_with_lists( return sprites -def get_sprites_at_point(point: Point, sprite_list: SpriteSequence[SpriteType]) -> List[SpriteType]: +def get_sprites_at_point(point: Point, sprite_list: SpriteSequence[SpriteType]) -> list[SpriteType]: """ Get a list of sprites at a particular point. This function sees if any sprite overlaps the specified point. If a sprite has a different center_x/center_y but touches the point, @@ -322,7 +318,7 @@ def get_sprites_at_point(point: Point, sprite_list: SpriteSequence[SpriteType]) def get_sprites_at_exact_point( point: Point, sprite_list: SpriteSequence[SpriteType] -) -> List[SpriteType]: +) -> list[SpriteType]: """ Get a list of sprites whose center_x, center_y match the given point. This does NOT return sprites that overlap the point, the center has to be an exact match. @@ -349,7 +345,7 @@ def get_sprites_at_exact_point( return [s for s in sprites_to_check if s.position == point] -def get_sprites_in_rect(rect: Rect, sprite_list: SpriteSequence[SpriteType]) -> List[SpriteType]: +def get_sprites_in_rect(rect: Rect, sprite_list: SpriteSequence[SpriteType]) -> list[SpriteType]: """ Get a list of sprites in a particular rectangle. This function sees if any sprite overlaps the specified rectangle. If a sprite has a different diff --git a/arcade/sprite_list/sprite_list.py b/arcade/sprite_list/sprite_list.py index f2ca56e606..8c07f359ff 100644 --- a/arcade/sprite_list/sprite_list.py +++ b/arcade/sprite_list/sprite_list.py @@ -11,16 +11,11 @@ from abc import abstractmethod from array import array from collections import deque +from collections.abc import Callable, Collection, Iterable, Iterator, Sized from typing import ( TYPE_CHECKING, Any, - Callable, ClassVar, - Collection, - Deque, - Iterable, - Iterator, - Sized, cast, ) @@ -241,7 +236,7 @@ def __init__( # Number of slots used in the index buffer self._sprite_index_slots = 0 # List of free slots in the sprite buffers. These are filled when sprites are removed. - self._sprite_buffer_free_slots: Deque[int] = deque() + self._sprite_buffer_free_slots: deque[int] = deque() # List of sprites in the sprite list self.sprite_list: list[SpriteType] = [] @@ -998,8 +993,8 @@ def update_animation(self, delta_time: float = 1 / 60, *args, **kwargs) -> None: def _get_center(self) -> tuple[float, float]: """Get the mean center coordinates of all sprites in the list.""" - x = sum((sprite.center_x for sprite in self.sprite_list)) / len(self.sprite_list) - y = sum((sprite.center_y for sprite in self.sprite_list)) / len(self.sprite_list) + x = sum(sprite.center_x for sprite in self.sprite_list) / len(self.sprite_list) + y = sum(sprite.center_y for sprite in self.sprite_list) / len(self.sprite_list) return x, y center = property(_get_center) @@ -1023,7 +1018,7 @@ def move(self, change_x: float, change_y: float) -> None: sprite.center_x += change_x sprite.center_y += change_y - def preload_textures(self, texture_list: Iterable["Texture"]) -> None: + def preload_textures(self, texture_list: Iterable[Texture]) -> None: """ Preload a set of textures that will be used for sprites in this sprite list. diff --git a/arcade/text.py b/arcade/text.py index a836d6b5b1..3ceec48bfb 100644 --- a/arcade/text.py +++ b/arcade/text.py @@ -4,7 +4,7 @@ from ctypes import c_int, c_ubyte from pathlib import Path -from typing import Any, Union +from typing import Any import pyglet @@ -98,7 +98,7 @@ def load_font(path: str | Path) -> None: pyglet.font.add_file(str(file_path)) -FontNameOrNames = Union[str, tuple[str, ...]] +FontNameOrNames = str | tuple[str, ...] def _attempt_font_name_resolution(font_name: FontNameOrNames) -> str: diff --git a/arcade/texture_atlas/atlas_array.py b/arcade/texture_atlas/atlas_array.py index 5f15eab310..1505843f63 100644 --- a/arcade/texture_atlas/atlas_array.py +++ b/arcade/texture_atlas/atlas_array.py @@ -4,7 +4,6 @@ from typing import ( TYPE_CHECKING, - Tuple, ) from .base import TextureAtlasBase @@ -23,13 +22,13 @@ class TextureArrayAtlas(TextureAtlasBase): layers (int): The number of layers (number of textures to store) """ - def __init__(self, ctx: ArcadeContext | None, size: Tuple[int, int], layers: int): + def __init__(self, ctx: ArcadeContext | None, size: tuple[int, int], layers: int): super().__init__(ctx) self._size = size self._layers = layers @property - def size(self) -> Tuple[int, int]: + def size(self) -> tuple[int, int]: """The texture size in pixels per layer.""" return self._size diff --git a/arcade/texture_atlas/atlas_default.py b/arcade/texture_atlas/atlas_default.py index 8223063939..63ebfa86a0 100644 --- a/arcade/texture_atlas/atlas_default.py +++ b/arcade/texture_atlas/atlas_default.py @@ -2,13 +2,13 @@ import contextlib import copy +from collections.abc import Sequence # import logging # import time from pathlib import Path from typing import ( TYPE_CHECKING, - Sequence, ) from weakref import WeakSet, WeakValueDictionary, finalize @@ -412,7 +412,7 @@ def _allocate_texture(self, texture: Texture) -> tuple[int, AtlasRegion]: return slot, texture_region - def _allocate_image(self, image_data: "ImageData") -> tuple[int, int, int, AtlasRegion]: + def _allocate_image(self, image_data: ImageData) -> tuple[int, int, int, AtlasRegion]: """ Attempts to allocate space for an image in the atlas or update the existing space for the image. @@ -557,7 +557,7 @@ def _remove_texture_by_identifiers(self, atlas_name: str, hash: str): self._textures_removed += 1 - def update_texture_image(self, texture: "Texture"): + def update_texture_image(self, texture: Texture): """ Updates the internal image of a texture in the atlas texture. The new image needs to be the exact same size as the original @@ -600,7 +600,7 @@ def get_texture_region_info(self, atlas_name: str) -> AtlasRegion: """ return self._texture_regions[atlas_name] - def get_texture_id(self, texture: "Texture") -> int: + def get_texture_id(self, texture: Texture) -> int: """ Get the internal id for a Texture in the atlas @@ -609,7 +609,7 @@ def get_texture_id(self, texture: "Texture") -> int: """ return self._texture_uvs.get_slot_or_raise(texture.atlas_name) - def has_texture(self, texture: "Texture") -> bool: + def has_texture(self, texture: Texture) -> bool: """ Check if a texture is already in the atlas. @@ -618,7 +618,7 @@ def has_texture(self, texture: "Texture") -> bool: """ return texture in self._textures - def has_unique_texture(self, texture: "Texture") -> bool: + def has_unique_texture(self, texture: Texture) -> bool: """ Check if the atlas already have a texture with the same image data and vertex order @@ -778,7 +778,7 @@ def use_uv_texture(self, unit: int = 0) -> None: @contextlib.contextmanager def render_into( self, - texture: "Texture", + texture: Texture, projection: tuple[float, float, float, float] | None = None, ): """ @@ -831,7 +831,7 @@ def render_into( fbo.viewport = 0, 0, *self._fbo.size prev_camera.use() - def read_texture_image_from_atlas(self, texture: "Texture") -> Image.Image: + def read_texture_image_from_atlas(self, texture: Texture) -> Image.Image: """ Read the pixel data for a texture directly from the atlas texture on the GPU. The contents of this image can be altered by rendering into the atlas and @@ -852,7 +852,7 @@ def read_texture_image_from_atlas(self, texture: "Texture") -> Image.Image: data = self.fbo.read(viewport=viewport, components=4) return Image.frombytes("RGBA", (region.width, region.height), data) - def update_texture_image_from_atlas(self, texture: "Texture") -> None: + def update_texture_image_from_atlas(self, texture: Texture) -> None: """ Update the Arcade Texture's internal image with the pixel data content from the atlas texture on the GPU. This can be useful if you render diff --git a/arcade/texture_atlas/base.py b/arcade/texture_atlas/base.py index 216238c19d..1a089f83da 100644 --- a/arcade/texture_atlas/base.py +++ b/arcade/texture_atlas/base.py @@ -251,7 +251,7 @@ def use_uv_texture(self, unit: int = 0) -> None: @contextlib.contextmanager def render_into( self, - texture: "Texture", + texture: Texture, projection: tuple[float, float, float, float] | None = None, ): """ diff --git a/arcade/texture_atlas/ref_counters.py b/arcade/texture_atlas/ref_counters.py index 6740f8b54f..197df53c1a 100644 --- a/arcade/texture_atlas/ref_counters.py +++ b/arcade/texture_atlas/ref_counters.py @@ -8,7 +8,7 @@ simply a texture using the same image and the same vertex order. """ -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING if TYPE_CHECKING: from arcade.texture import ImageData, Texture @@ -23,7 +23,7 @@ class ImageDataRefCounter: """ def __init__(self) -> None: - self._data: Dict[str, int] = {} + self._data: dict[str, int] = {} self._num_decref = 0 def inc_ref(self, image_data: "ImageData") -> None: @@ -123,7 +123,7 @@ class UniqueTextureRefCounter: """ def __init__(self) -> None: - self._data: Dict[str, int] = {} + self._data: dict[str, int] = {} self._num_decref = 0 def inc_ref(self, image_data: "Texture") -> None: diff --git a/arcade/texture_atlas/region.py b/arcade/texture_atlas/region.py index 8b14eebb49..86de2ae3d3 100644 --- a/arcade/texture_atlas/region.py +++ b/arcade/texture_atlas/region.py @@ -120,12 +120,10 @@ def verify_image_size(self, image_data: ImageData): """ if image_data.size != (self.width, self.height): raise RuntimeError( - ( - f"ImageData '{image_data.hash}' change their internal image " - f"size from {self.width}x{self.height} to " - f"{image_data.width}x{image_data.height}. " - "It's not possible to fit this into the old allocated area in the atlas. " - ) + f"ImageData '{image_data.hash}' change their internal image " + f"size from {self.width}x{self.height} to " + f"{image_data.width}x{image_data.height}. " + "It's not possible to fit this into the old allocated area in the atlas. " ) def __repr__(self) -> str: diff --git a/arcade/texture_atlas/uv_data.py b/arcade/texture_atlas/uv_data.py index 1cd439c566..8fe4c7576f 100644 --- a/arcade/texture_atlas/uv_data.py +++ b/arcade/texture_atlas/uv_data.py @@ -6,7 +6,7 @@ from array import array from collections import deque -from typing import TYPE_CHECKING, Dict +from typing import TYPE_CHECKING from .base import ( UV_TEXTURE_WIDTH, @@ -60,7 +60,7 @@ def __init__(self, ctx: ArcadeContext, capacity: int): # Python resources: data + tracker for slots # 8 floats per texture (4 x vec2 coordinates) self._data = array("f", [0] * self._num_slots * 8) - self._slots: Dict[str, int] = dict() + self._slots: dict[str, int] = dict() self._slots_free = deque(i for i in range(0, self._num_slots)) def clone_with_slots(self) -> UVData: @@ -89,7 +89,7 @@ def num_free_slots(self) -> int: return len(self._slots_free) @property - def texture(self) -> "Texture2D": + def texture(self) -> Texture2D: """The opengl texture containing the texture coordinates""" return self._texture @@ -124,11 +124,9 @@ def get_existing_or_free_slot(self, name: str) -> int: return slot except IndexError: raise Exception( - ( - "No more free slots in the UV texture." - f"Max number of textures: {self._num_slots}." - "Consider creating a texture atlas with a larger capacity." - ) + "No more free slots in the UV texture." + f"Max number of textures: {self._num_slots}." + "Consider creating a texture atlas with a larger capacity." ) def free_slot_by_name(self, name: str) -> None: diff --git a/arcade/tilemap/tilemap.py b/arcade/tilemap/tilemap.py index e81e5efc19..3828c6c9c8 100644 --- a/arcade/tilemap/tilemap.py +++ b/arcade/tilemap/tilemap.py @@ -13,9 +13,9 @@ import math import os from collections import OrderedDict -from collections.abc import Sequence +from collections.abc import Callable, Sequence from pathlib import Path -from typing import TYPE_CHECKING, Any, Callable, List, cast +from typing import TYPE_CHECKING, Any, cast import pytiled_parser import pytiled_parser.tiled_object @@ -373,7 +373,7 @@ def get_tilemap_layer(self, layer_path: str) -> pytiled_parser.Layer | None: """ assert isinstance(layer_path, str) - def _get_tilemap_layer(my_path: List[str], layers): + def _get_tilemap_layer(my_path: list[str], layers): layer_name = my_path.pop(0) for my_layer in layers: if my_layer.name == layer_name: @@ -670,7 +670,7 @@ def _create_sprite_from_tile( def _process_image_layer( self, layer: pytiled_parser.ImageLayer, - texture_atlas: "DefaultTextureAtlas", + texture_atlas: DefaultTextureAtlas, scaling: float = 1.0, use_spatial_hash: bool = False, hit_box_algorithm: HitBoxAlgorithm | None = None, @@ -758,7 +758,7 @@ def _process_image_layer( def _process_tile_layer( self, layer: pytiled_parser.TileLayer, - texture_atlas: "DefaultTextureAtlas", + texture_atlas: DefaultTextureAtlas, scaling: float = 1.0, use_spatial_hash: bool = False, hit_box_algorithm: HitBoxAlgorithm | None = None, @@ -786,11 +786,9 @@ def _process_tile_layer( tile = self._get_tile_by_gid(item) if tile is None: raise ValueError( - ( - f"Couldn't find tile for item {item} in layer " - f"'{layer.name}' in file '{self.tiled_map.map_file}'" - f"at ({column_index}, {row_index})." - ) + f"Couldn't find tile for item {item} in layer " + f"'{layer.name}' in file '{self.tiled_map.map_file}'" + f"at ({column_index}, {row_index})." ) my_sprite = self._create_sprite_from_tile( @@ -836,7 +834,7 @@ def _process_tile_layer( def _process_object_layer( self, layer: pytiled_parser.ObjectLayer, - texture_atlas: "DefaultTextureAtlas", + texture_atlas: DefaultTextureAtlas, scaling: float = 1.0, use_spatial_hash: bool = False, hit_box_algorithm: HitBoxAlgorithm | None = None, diff --git a/arcade/types/__init__.py b/arcade/types/__init__.py index da08112cd0..04a97f5ec9 100644 --- a/arcade/types/__init__.py +++ b/arcade/types/__init__.py @@ -24,7 +24,8 @@ # flake8: noqa: E402 import sys from pathlib import Path -from typing import Any, NamedTuple, Union, TYPE_CHECKING, TypeVar, Iterable, Protocol +from typing import Any, NamedTuple, TYPE_CHECKING, TypeVar, Protocol, Union +from collections.abc import Iterable from pytiled_parser import Properties @@ -43,7 +44,7 @@ # use an ABC which registers virtual subclasses. This will not work # with ctypes.Array since virtual subclasses must be concrete. # See: https://peps.python.org/pep-0688/ - BufferProtocol = Union[ByteString, memoryview, array, ctypes.Array] + BufferProtocol = ByteString | memoryview | array | ctypes.Array # Since it's used everywhere, we'll prevent partial submodule @@ -143,7 +144,7 @@ _T = TypeVar("_T") -OneOrIterableOf = Union[_T, Iterable[_T]] +OneOrIterableOf = Union[_T, Iterable[_T]] # noqa: UP007 """Either an instance of something or an iterable of them. When writing loading code which is not performance critical, @@ -225,9 +226,9 @@ def __mul__(self, value: _T_contra, /) -> _T_co: ... # Path handling -PathLike = Union[str, Path, bytes] +PathLike = str | Path | bytes _POr = TypeVar("_POr") # Allows PathOr[TypeNameHere] syntax -PathOr = Union[PathLike, _POr] +PathOr = Union[PathLike, _POr] # noqa: UP007 # Specific utility resource aliases with type imports @@ -241,7 +242,7 @@ def __mul__(self, value: _T_contra, /) -> _T_co: ... class TiledObject(NamedTuple): """Object in a tilemaps""" - shape: Union[Point, PointList, tuple[int, int, int, int]] + shape: Point | PointList | tuple[int, int, int, int] """Shape of the object""" properties: Properties | None = None """Properties of the object""" @@ -260,4 +261,4 @@ class SupportsDunderGT(Protocol[_T_contra]): def __gt__(self, other: _T_contra, /) -> bool: ... -SupportsRichComparison = Union[SupportsDunderLT[Any], SupportsDunderGT[Any]] +SupportsRichComparison = SupportsDunderLT[Any] | SupportsDunderGT[Any] diff --git a/arcade/types/color.py b/arcade/types/color.py index 98ee2284f1..685e7cc9e4 100644 --- a/arcade/types/color.py +++ b/arcade/types/color.py @@ -21,9 +21,10 @@ from __future__ import annotations import random -from typing import Iterable, TypeVar, Union +from collections.abc import Iterable +from typing import Final, TypeVar -from typing_extensions import Final, Self +from typing_extensions import Self from arcade.exceptions import ByteRangeError, IntOutsideRangeError, NormalizedRangeError @@ -63,12 +64,12 @@ # Color type aliases. -ChannelType = TypeVar("ChannelType") +ChannelType = TypeVar("ChannelType", int, float) # Generic color aliases RGB = tuple[ChannelType, ChannelType, ChannelType] RGBA = tuple[ChannelType, ChannelType, ChannelType, ChannelType] -RGBOrA = Union[RGB[ChannelType], RGBA[ChannelType]] +RGBOrA = RGB[ChannelType] | RGBA[ChannelType] # Specific color aliases RGB255 = RGB[int] diff --git a/arcade/types/numbers.py b/arcade/types/numbers.py index 6ece433e08..318cab7b31 100644 --- a/arcade/types/numbers.py +++ b/arcade/types/numbers.py @@ -5,10 +5,8 @@ circular imports or partially initialized modules. """ -from typing import Union - #: 1. Makes pyright happier while also telling readers #: 2. Tells readers we're converting any ints to floats -AsFloat = Union[float, int] +AsFloat = float | int __all__ = ["AsFloat"] diff --git a/arcade/types/vector_like.py b/arcade/types/vector_like.py index 782694468c..1965786585 100644 --- a/arcade/types/vector_like.py +++ b/arcade/types/vector_like.py @@ -7,17 +7,17 @@ """ -from typing import Sequence, Union +from collections.abc import Sequence from pyglet.math import Vec2, Vec3 from arcade.types.numbers import AsFloat #: Matches both :py:class:`~pyglet.math.Vec2` and tuples of two numbers. -Point2 = Union[tuple[AsFloat, AsFloat], Vec2] +Point2 = tuple[AsFloat, AsFloat] | Vec2 #: Matches both :py:class:`~pyglet.math.Vec3` and tuples of three numbers. -Point3 = Union[tuple[AsFloat, AsFloat, AsFloat], Vec3] +Point3 = tuple[AsFloat, AsFloat, AsFloat] | Vec3 #: Matches any 2D or 3D point, including: @@ -32,7 +32,7 @@ #: This works the same way as :py:attr:`arcade.types.RGBOrA255` to #: annotate RGB tuples, RGBA tuples, and :py:class:`tuple` or a #: :py:class:`Color` instances. -Point = Union[Point2, Point3] +Point = Point2 | Point3 PointList = Sequence[Point] Point2List = Sequence[Point2] diff --git a/arcade/utils.py b/arcade/utils.py index 547820d47e..25595390c3 100644 --- a/arcade/utils.py +++ b/arcade/utils.py @@ -6,10 +6,10 @@ import platform import sys -from collections.abc import MutableSequence +from collections.abc import Callable, Generator, Iterable, MutableSequence, Sequence from itertools import chain from pathlib import Path -from typing import Any, Callable, Generator, Generic, Iterable, Sequence, Type, TypeVar +from typing import Any, Generic, TypeVar from arcade.types import AsFloat, Point2 @@ -28,7 +28,7 @@ # Since this module forbids importing from the rest of # Arcade, we make our own local type variables. _T = TypeVar("_T") -_TType = TypeVar("_TType", bound=Type) +_TType = TypeVar("_TType", bound=type) class Chain(Generic[_T]): @@ -49,8 +49,7 @@ def __init__(self, *components: Sequence[_T]): self.components: list[Sequence[_T]] = list(components) def __iter__(self) -> Generator[_T, None, None]: - for item in chain.from_iterable(self.components): - yield item + yield from chain.from_iterable(self.components) def as_type(item: Any) -> type: @@ -242,7 +241,7 @@ def __copy__(self): # noqa f"you may implement it on a custom subclass." ) - decorated_type.__copy__ = __copy__ + decorated_type.__copy__ = __copy__ # type: ignore def __deepcopy__(self, memo): # noqa raise NotImplementedError( @@ -250,7 +249,7 @@ def __deepcopy__(self, memo): # noqa f" but you may implement it on a custom subclass." ) - decorated_type.__deepcopy__ = __deepcopy__ + decorated_type.__deepcopy__ = __deepcopy__ # type: ignore return decorated_type @@ -306,7 +305,7 @@ def unpack_asfloat_or_point(value: AsFloat | Point2) -> Point2: A Point2 that is either equal to value, or is equal to (value, value) """ - if isinstance(value, (float, int)): + if isinstance(value, float | int): x = y = value else: try: diff --git a/arcade/window_commands.py b/arcade/window_commands.py index 92013ed513..99d2f41c05 100644 --- a/arcade/window_commands.py +++ b/arcade/window_commands.py @@ -9,7 +9,8 @@ import gc import os import time -from typing import TYPE_CHECKING, Callable +from collections.abc import Callable +from typing import TYPE_CHECKING import pyglet @@ -53,7 +54,7 @@ def get_display_size(screen_id: int = 0) -> tuple[int, int]: return screen.width, screen.height -def get_window() -> "Window": +def get_window() -> Window: """ Return a handle to the current window. @@ -180,12 +181,10 @@ def start_render(pixelated=False, blend=True) -> None: window = get_window() if window._start_finish_render_data is not None: raise RuntimeError( - ( - "start_render() can only be called once during the application's lifetime " - "and should only be used when calling draw functions at module level in " - "a simple script to produce a static image. If you are seeing this error " - "you likely intended to call clear() instead." - ) + "start_render() can only be called once during the application's lifetime " + "and should only be used when calling draw functions at module level in " + "a simple script to produce a static image. If you are seeing this error " + "you likely intended to call clear() instead." ) window._start_finish_render_data = StartFinishRenderData(pixelated=pixelated, blend=blend) diff --git a/benchmarks/sprite/main.py b/benchmarks/sprite/main.py index e7aaf9bc71..0e9001de89 100644 --- a/benchmarks/sprite/main.py +++ b/benchmarks/sprite/main.py @@ -1,6 +1,7 @@ """ Quick and dirty system measuring differences between two sprite classes. """ + import gc import math import timeit @@ -17,9 +18,19 @@ MEASUREMENT_CONFIG = [ {"name": "populate", "number": N, "measure_method": "populate", "post_methods": ["flush"]}, {"name": "scale_set", "number": N, "measure_method": "scale_set", "post_methods": []}, - {"name": "scale_set_uniform", "number": N, "measure_method": "scale_set_uniform", "post_methods": []}, + { + "name": "scale_set_uniform", + "number": N, + "measure_method": "scale_set_uniform", + "post_methods": [], + }, {"name": "scale_mult", "number": N, "measure_method": "scale_mult", "post_methods": []}, - {"name": "scale_mult_uniform", "number": N, "measure_method": "scale_mult_uniform", "post_methods": []}, + { + "name": "scale_mult_uniform", + "number": N, + "measure_method": "scale_mult_uniform", + "post_methods": [], + }, ] @@ -94,6 +105,7 @@ def scale_mult(self): class SpriteCollectionA(SpriteCollection): sprite_type = SpriteA + class SpriteCollectionB(SpriteCollection): sprite_type = SpriteB @@ -138,11 +150,11 @@ def main(): a = SpriteCollectionA() b = SpriteCollectionB() - m1 = measure_sprite_collection(a) + _ = measure_sprite_collection(a) gc_until_nothing() - m2 = measure_sprite_collection(b) + _ = measure_sprite_collection(b) # FIXME: Compare measurements -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/benchmarks/sprite/sprite_alt.py b/benchmarks/sprite/sprite_alt.py index db09941b4e..61d79dded6 100644 --- a/benchmarks/sprite/sprite_alt.py +++ b/benchmarks/sprite/sprite_alt.py @@ -1,4 +1,5 @@ -from typing import TYPE_CHECKING, Any, Iterable, TypeVar +from typing import TYPE_CHECKING, Any, TypeVar +from collections.abc import Iterable import arcade from arcade.color import BLACK, WHITE @@ -63,12 +64,12 @@ def __init__( self._depth = 0.0 self._texture = texture width, height = texture.size - self._scale = (scale, scale) if isinstance(scale, (float, int)) else scale + self._scale = (scale, scale) if isinstance(scale, float | int) else scale self._width = width * self._scale[0] self._height = height * self._scale[1] self._visible = bool(visible) self._color: Color = WHITE - self.sprite_lists: list["SpriteList"] = [] + self.sprite_lists: list[SpriteList] = [] """The sprite lists this sprite is a member of""" # Core properties we don't use, but spritelist expects it @@ -417,7 +418,6 @@ def rgb(self) -> tuple[int, int, int]: @rgb.setter def rgb(self, color: RGBOrA255): - # Fast validation of size by unpacking channel values try: r, g, b, *_a = color @@ -426,10 +426,8 @@ def rgb(self, color: RGBOrA255): except ValueError: # It's always a length issue raise ValueError( - ( - f"{self.__class__.__name__},rgb takes 3 or 4 channel" - f" colors, but got {len(color)} channels" - ) + f"{self.__class__.__name__},rgb takes 3 or 4 channel" + f" colors, but got {len(color)} channels" ) # Unpack to avoid index / . overhead & prep for repack diff --git a/make.py b/make.py index ced4ec11dd..463de5daf0 100755 --- a/make.py +++ b/make.py @@ -17,7 +17,8 @@ from contextlib import contextmanager from pathlib import Path from shutil import rmtree, which -from typing import Generator, Union +from typing import Union +from collections.abc import Generator PathLike = Union[Path, str, bytes] diff --git a/pyproject.toml b/pyproject.toml index 3a995d57c2..4302c619df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -108,6 +108,7 @@ lint.select = [ # Whitespace linting must be re-enabled manually for ruff # see https://beta.ruff.rs/docs/configuration/#using-pyprojecttoml "W", + # "UP", wait for arcade.gl abstraction merge ] [tool.ruff.format]