From 6e8259d5190997ec2b44001d8aac4834f1595c6a Mon Sep 17 00:00:00 2001 From: Paul <36696816+pushfoo@users.noreply.github.com> Date: Thu, 18 Jul 2024 07:57:35 -0400 Subject: [PATCH] Pyglet2.1dev$VERSION updates (#2226) * Bump to pyglet == 2.1dev3 * Remove del trick for getting future vec behavior * Update to options object * Switch arcade/__init__.py to use OOP options object * Convert applications.py to OOP pyglet options * Convert doc on headless to use options object * Fix a comment in arcade/__init__.py * Update tests/__init__.py * Fix formatting * Type compatibility fix in controller db loading * Use pyglet.media.Source instead of a Union type * Fix color typing in arcade.Text * Ignore over-strict pyright Literal opinion * Attempt to patch up the font loader * Use new-style | None in application.Window.__init__ * Update some typing for set_fullscreen * Use new-style | None * Add casting and TODO on resolving screen coord issues upstream * Mention rounding issue in set_fullscreen * Add temporary type ignore for font_size * Update type + doc for arcade.text bold support * Update top-level docstring * Update bold setter & getter * Update bold return types * Temporarily use pyglet development branch directly * Use development pyglet in rolling release mode + comment it * Add commented-out future pyglet == 2.1dev4 release we can swap to it later * Formatting * Use newly released pyglet==2.1dev4 * Projection funcs: remove inner tuple for Mat4 init * add dot to pyproject.toml (no idea why pip resolves that) * Use Vec3 for now in perspective example * Centralize headless + add some type: ignore * Centralize headless * Add some type: ignore since pyglet did vague .pyi * type: ignore in sound.py * Whoops, forgot what file I'm in * Replace todo with # type: ignore # pending syntax * Awful temp fix for Text.font_name * Formatting for sound * Abomination of a test fix * Import sorting for arcade/__init__.py * Revert "Abomination of a test fix" (Test not actually fixed) This reverts commit c59d674c7aefc08cd1aee20eda24d20b598ac890. * Maybe fix the import test? * Temp fix for pyglet typing all things as float for some reason * Fix another instance of pyglet making everything a float * Out of patience for pyglet import / metaclass / method resolution * Fix rebase issue in casting * Type ignore window issues * Tweak pyproject.toml * Ignore pyglet Window type The class is dynamically assigned at runtime so pyright can't handle it. * Wrong typing for screen * format issue * Bump pyright * Temp ignore EventDispatcher issue --------- Co-authored-by: Einar Forselv --- arcade/__init__.py | 29 +++++-------- arcade/application.py | 49 +++++++++++++--------- arcade/camera/projection_functions.py | 12 +++--- arcade/context.py | 5 ++- arcade/examples/perspective.py | 6 +-- arcade/gl/context.py | 5 ++- arcade/gui/constructs.py | 4 +- arcade/gui/ui_manager.py | 2 +- arcade/gui/widgets/__init__.py | 6 +-- arcade/gui/widgets/dropdown.py | 2 +- arcade/gui/widgets/slider.py | 2 +- arcade/gui/widgets/toggle.py | 2 +- arcade/sound.py | 9 ++-- arcade/text.py | 60 ++++++++++++++++++--------- doc/programming_guide/headless.rst | 10 +++-- pyproject.toml | 26 +++++++----- tests/__init__.py | 2 +- tests/unit/test_arcade.py | 7 +++- 18 files changed, 140 insertions(+), 98 deletions(-) diff --git a/arcade/__init__.py b/arcade/__init__.py index 3e2833748..ad401effb 100644 --- a/arcade/__init__.py +++ b/arcade/__init__.py @@ -10,7 +10,7 @@ # Error out if we import Arcade with an incompatible version of Python. import sys import os -from typing import Optional +from typing import Final, Optional from pathlib import Path @@ -58,29 +58,21 @@ def configure_logging(level: Optional[int] = None): # noinspection PyPep8 import pyglet -# TODO: Remove ASAP after pyglet >= 2.1dev2 is out -if pyglet.version == "2.1.dev2": - # Temporary monkeypatch via deletion since dev2 still includes - # overly-specific __eq__ behavior. Later pyglet commits restore - # equality with same-valued tuples by deleting the __eq__ methods. - from pyglet import math as _pyglet_math - - del _pyglet_math.Vec2.__eq__ - del _pyglet_math.Vec3.__eq__ - del _pyglet_math.Vec4.__eq__ # Env variable shortcut for headless mode -if os.environ.get("ARCADE_HEADLESS"): - pyglet.options["headless"] = True +headless: Final[bool] = bool(os.environ.get("ARCADE_HEADLESS")) +if headless: + pyglet.options.headless = headless # type: ignore # pending https://github.com/pyglet/pyglet/issues/1164 + from arcade import utils # Disable shadow window on macs and in headless mode. if sys.platform == "darwin" or os.environ.get("ARCADE_HEADLESS") or utils.is_raspberry_pi(): - pyglet.options["shadow_window"] = False + pyglet.options.shadow_window = False # type: ignore # pending https://github.com/pyglet/pyglet/issues/1164 # Use the old gdi fonts on windows until directwrite is fast/stable -# pyglet.options['win32_gdi_font'] = True +# pyglet.options.win32_gdi_font = True # Imports from modules that don't do anything circular @@ -152,7 +144,7 @@ def configure_logging(level: Optional[int] = None): from .screenshot import get_pixel # We don't have joysticks game controllers in headless mode -if not pyglet.options["headless"]: +if not headless: # type: ignore from .joysticks import get_game_controllers from .joysticks import get_joysticks from .controller import ControllerManager @@ -407,11 +399,12 @@ def configure_logging(level: Optional[int] = None): # Piggyback on pyglet's doc run detection if not getattr(sys, "is_pyglet_doc_run", False): # Load additional game controller mappings to Pyglet - if not pyglet.options["headless"]: + if not headless: try: import pyglet.input.controller mappings_file = resources.resolve(":system:gamecontrollerdb.txt") - pyglet.input.controller.add_mappings_from_file(mappings_file) + # TODO: remove string conversion once fixed upstream + pyglet.input.controller.add_mappings_from_file(str(mappings_file)) except AssertionError: pass diff --git a/arcade/application.py b/arcade/application.py index d6d202137..93f3a2e9e 100644 --- a/arcade/application.py +++ b/arcade/application.py @@ -132,14 +132,14 @@ def __init__( self, width: int = 1280, height: int = 720, - title: Optional[str] = "Arcade Window", + title: str | None = "Arcade Window", fullscreen: bool = False, resizable: bool = False, update_rate: float = 1 / 60, antialiasing: bool = True, gl_version: tuple[int, int] = (3, 3), - screen: Optional[pyglet.display.Screen] = None, - style: Optional[str] = pyglet.window.Window.WINDOW_STYLE_DEFAULT, + screen: pyglet.display.Screen | None = None, + style: str | None = pyglet.window.Window.WINDOW_STYLE_DEFAULT, visible: bool = True, vsync: bool = False, gc_mode: str = "context_gc", @@ -149,7 +149,7 @@ def __init__( gl_api: str = "gl", draw_rate: float = 1 / 60, fixed_rate: float = 1.0 / 60.0, - fixed_frame_cap: Optional[int] = None, + fixed_frame_cap: int | None = None, ) -> None: # In certain environments we can't have antialiasing/MSAA enabled. # Detect replit environment @@ -161,7 +161,7 @@ def __init__( gl_version = 3, 1 gl_api = "gles" - self.headless: bool = pyglet.options.get("headless") is True + self.headless: bool = arcade.headless """If True, the window is running in headless mode.""" config = None @@ -171,7 +171,7 @@ def __init__( config = pyglet.gl.Config( major_version=gl_version[0], minor_version=gl_version[1], - opengl_api=gl_api, + opengl_api=gl_api, # type: ignore # pending: upstream fix double_buffer=True, sample_buffers=1, samples=samples, @@ -183,7 +183,7 @@ def __init__( alpha_size=8, ) display = pyglet.display.get_display() - screen = display.get_default_screen() + screen = display.get_default_screen() # type: ignore # pending: resolve upstream type tricks if screen: config = screen.get_best_config(config) except pyglet.window.NoSuchConfigException: @@ -195,7 +195,7 @@ def __init__( config = pyglet.gl.Config( major_version=gl_version[0], minor_version=gl_version[1], - opengl_api=gl_api, + opengl_api=gl_api, # type: ignore # pending: upstream fix double_buffer=True, depth_size=24, stencil_size=8, @@ -215,9 +215,10 @@ def __init__( visible=visible, style=style, ) - self.register_event_type("on_update") - self.register_event_type("on_action") - self.register_event_type("on_fixed_update") + # pending: weird import tricks resolved + self.register_event_type("on_update") # type: ignore + self.register_event_type("on_action") # type: ignore + self.register_event_type("on_fixed_update") # type: ignore except pyglet.window.NoSuchConfigException: raise NoOpenGLException( "Unable to create an OpenGL 3.3+ context. " @@ -291,7 +292,7 @@ def __init__( if enable_polling: self.keyboard = pyglet.window.key.KeyStateHandler() - if pyglet.options["headless"]: + if arcade.headless: self.push_handlers(self.keyboard) else: @@ -412,10 +413,10 @@ def close(self) -> None: def set_fullscreen( self, fullscreen: bool = True, - screen: Optional["Window"] = None, - mode: Optional[ScreenMode] = None, - width: Optional[int] = None, - height: Optional[int] = None, + screen=None, + mode: ScreenMode | None = None, + width: float | None = None, + height: float | None = None, ) -> None: """ Change the fullscreen status of the window. @@ -438,10 +439,18 @@ def set_fullscreen( have been obtained by enumerating `Screen.get_modes`. If None, an appropriate mode will be selected from the given `width` and `height`. - width (int, optional): Override the width of the window - height (int, optional): Override the height of the window - """ - super().set_fullscreen(fullscreen, screen, mode, width, height) + width: Override the width of the window. Will be rounded to + :py:attr:`int`. + height: Override the height of the window. Will be rounded to + :py:attr:`int`. + """ + # fmt: off + super().set_fullscreen( + fullscreen, screen, mode, + # TODO: resolve the upstream int / float screen coord issue + None if width is None else int(width), + None if height is None else int(height)) + # fmt: on def center_window(self) -> None: """Center the window on your desktop.""" diff --git a/arcade/camera/projection_functions.py b/arcade/camera/projection_functions.py index 25d648c76..2d7dc2b1b 100644 --- a/arcade/camera/projection_functions.py +++ b/arcade/camera/projection_functions.py @@ -27,12 +27,12 @@ def generate_view_matrix(camera_data: CameraData) -> Mat4: po = Vec3(*camera_data.position) # fmt: off - return Mat4(( + return Mat4( ri.x, up.x, -fo.x, 0.0, ri.y, up.y, -fo.y, 0.0, ri.z, up.z, -fo.z, 0.0, -ri.dot(po), -up.dot(po), fo.dot(po), 1.0 - )) + ) # fmt: on @@ -69,12 +69,12 @@ def generate_orthographic_matrix( tz = -(z_far + z_near) / depth # fmt: off - return Mat4(( + return Mat4( sx, 0.0, 0.0, 0.0, 0.0, sy, 0.0, 0.0, 0.0, 0.0, sz, 0.0, tx, ty, tz, 1.0 - )) + ) # fmt: on @@ -110,12 +110,12 @@ def generate_perspective_matrix( h = 2 * z_near / height # fmt: off - return Mat4(( + return Mat4( w, 0, 0, 0, 0, h, 0, 0, 0, 0, q, -1, 0, 0, qn, 0 - )) + ) # fmt: on diff --git a/arcade/context.py b/arcade/context.py index f59c86120..dbb8e8395 100644 --- a/arcade/context.py +++ b/arcade/context.py @@ -48,7 +48,10 @@ class ArcadeContext(Context): atlas_size: tuple[int, int] = 512, 512 def __init__( - self, window: pyglet.window.Window, gc_mode: str = "context_gc", gl_api: str = "gl" + self, + window: pyglet.window.Window, # type: ignore + gc_mode: str = "context_gc", + gl_api: str = "gl", ) -> None: super().__init__(window, gc_mode=gc_mode, gl_api=gl_api) diff --git a/arcade/examples/perspective.py b/arcade/examples/perspective.py index 315c7e62a..63dea6f7a 100644 --- a/arcade/examples/perspective.py +++ b/arcade/examples/perspective.py @@ -18,7 +18,7 @@ from array import array import arcade -from pyglet.math import Mat4 +from pyglet.math import Mat4, Vec3 from arcade.gl import BufferDescription @@ -121,8 +121,8 @@ def on_draw(self): self.fbo.color_attachments[0].use(unit=0) # Move the plane into camera view and rotate it - translate = Mat4.from_translation((0, 0, -2)) - rotate = Mat4.from_rotation(self.time / 2, (1, 0, 0)) + translate = Mat4.from_translation(Vec3(0, 0, -2)) + rotate = Mat4.from_rotation(self.time / 2, Vec3(1, 0, 0)) self.program["model"] = translate @ rotate # Scroll the texture coordinates diff --git a/arcade/gl/context.py b/arcade/gl/context.py index 57945a93e..b540937c4 100644 --- a/arcade/gl/context.py +++ b/arcade/gl/context.py @@ -183,7 +183,10 @@ class Context: _valid_apis = ("gl", "gles") def __init__( - self, window: pyglet.window.Window, gc_mode: str = "context_gc", gl_api: str = "gl" + self, + window: pyglet.window.Window, # type: ignore + gc_mode: str = "context_gc", + gl_api: str = "gl", ): self._window_ref = weakref.ref(window) if gl_api not in self._valid_apis: diff --git a/arcade/gui/constructs.py b/arcade/gui/constructs.py index ccd218a80..75899f399 100644 --- a/arcade/gui/constructs.py +++ b/arcade/gui/constructs.py @@ -46,7 +46,7 @@ def __init__( raise ValueError("At least a single value has to be available for `buttons`") super().__init__(size_hint=(1, 1)) - self.register_event_type("on_action") + self.register_event_type("on_action") # type: ignore # https://github.com/pyglet/pyglet/pull/1173 # noqa space = 20 @@ -136,7 +136,7 @@ def __init__( space_between=space_between, style=style, ) - self.register_event_type("on_action") + self.register_event_type("on_action") # type: ignore # https://github.com/pyglet/pyglet/pull/1173 # noqa self.button_factory = button_factory diff --git a/arcade/gui/ui_manager.py b/arcade/gui/ui_manager.py index 4005fbe62..2c263dbb6 100644 --- a/arcade/gui/ui_manager.py +++ b/arcade/gui/ui_manager.py @@ -99,7 +99,7 @@ def __init__(self, window: Optional[arcade.Window] = None): self._render_to_surface_camera = arcade.Camera2D() # this camera is used for rendering the UI and should not be changed by the user - self.register_event_type("on_event") + self.register_event_type("on_event") # type: ignore # https://github.com/pyglet/pyglet/pull/1173 # noqa def add(self, widget: W, *, index=None, layer=0) -> W: """ diff --git a/arcade/gui/widgets/__init__.py b/arcade/gui/widgets/__init__.py index ef38d6261..495ac3d31 100644 --- a/arcade/gui/widgets/__init__.py +++ b/arcade/gui/widgets/__init__.py @@ -99,8 +99,8 @@ def __init__( self.size_hint_min = size_hint_min self.size_hint_max = size_hint_max - self.register_event_type("on_event") - self.register_event_type("on_update") + self.register_event_type("on_event") # type: ignore # https://github.com/pyglet/pyglet/pull/1173 # noqa + self.register_event_type("on_update") # type: ignore # https://github.com/pyglet/pyglet/pull/1173 # noqa for child in children: self.add(child) @@ -516,7 +516,7 @@ def __init__( size_hint_max=size_hint_max, **kwargs, ) - self.register_event_type("on_click") + self.register_event_type("on_click") # type: ignore self.interaction_buttons = interaction_buttons diff --git a/arcade/gui/widgets/dropdown.py b/arcade/gui/widgets/dropdown.py index 8eca4449c..3719d7e37 100644 --- a/arcade/gui/widgets/dropdown.py +++ b/arcade/gui/widgets/dropdown.py @@ -100,7 +100,7 @@ def __init__( # add children after super class setup self.add(self._default_button) - self.register_event_type("on_change") + self.register_event_type("on_change") # type: ignore # https://github.com/pyglet/pyglet/pull/1173 # noqa self.with_border(color=arcade.color.RED) diff --git a/arcade/gui/widgets/slider.py b/arcade/gui/widgets/slider.py index 185d3edf5..d20a60d28 100644 --- a/arcade/gui/widgets/slider.py +++ b/arcade/gui/widgets/slider.py @@ -76,7 +76,7 @@ def __init__( bind(self, "pressed", self.trigger_render) bind(self, "disabled", self.trigger_render) - self.register_event_type("on_change") + self.register_event_type("on_change") # type: ignore # https://github.com/pyglet/pyglet/pull/1173 # noqa def _x_for_value(self, value: float): """Provides the x coordinate for the given value.""" diff --git a/arcade/gui/widgets/toggle.py b/arcade/gui/widgets/toggle.py index 647c712a2..150658a36 100644 --- a/arcade/gui/widgets/toggle.py +++ b/arcade/gui/widgets/toggle.py @@ -72,7 +72,7 @@ def __init__( ) self.value = value - self.register_event_type("on_change") + self.register_event_type("on_change") # type: ignore # https://github.com/pyglet/pyglet/pull/1173 # noqa bind(self, "value", self.trigger_render) bind(self, "value", self._dispatch_on_change_event) diff --git a/arcade/sound.py b/arcade/sound.py index ac6d20396..a0d363b11 100644 --- a/arcade/sound.py +++ b/arcade/sound.py @@ -11,15 +11,16 @@ from typing import Optional, Union import pyglet +from pyglet.media import Source from arcade.resources import resolve if os.environ.get("ARCADE_SOUND_BACKENDS"): - pyglet.options["audio"] = tuple( + pyglet.options.audio = tuple( # type: ignore v.strip() for v in os.environ["ARCADE_SOUND_BACKENDS"].split(",") ) else: - pyglet.options["audio"] = ("openal", "xaudio2", "directsound", "pulse", "silent") + pyglet.options.audio = ("openal", "xaudio2", "directsound", "pulse", "silent") # type: ignore import pyglet.media as media @@ -39,9 +40,7 @@ def __init__(self, file_name: Union[str, Path], streaming: bool = False): raise FileNotFoundError(f"The sound file '{file_name}' is not a file or can't be read.") self.file_name = str(file_name) - self.source: Union[media.StaticSource, media.StreamingSource] = media.load( - self.file_name, streaming=streaming - ) + self.source: Source = media.load(self.file_name, streaming=streaming) if self.source.duration is None: raise ValueError( diff --git a/arcade/text.py b/arcade/text.py index 928bccf92..f662b376f 100644 --- a/arcade/text.py +++ b/arcade/text.py @@ -45,9 +45,8 @@ def load_font(path: Union[str, Path]) -> None: FontNameOrNames = Union[str, tuple[str, ...]] -def _attempt_font_name_resolution(font_name: FontNameOrNames) -> FontNameOrNames: - """ - Attempt to resolve a tuple of font names. +def _attempt_font_name_resolution(font_name: FontNameOrNames) -> str: + """Attempt to resolve a font name. Preserves the original logic of this section, even though it doesn't seem to make sense entirely. Comments are an attempt @@ -83,8 +82,12 @@ def _attempt_font_name_resolution(font_name: FontNameOrNames) -> FontNameOrNames except FileNotFoundError: pass - # failed to find it ourselves, hope pyglet can make sense of it - return font_name + # failed to find it ourselves, hope pyglet can make sense of it + # Note this is the best approximation of what I unerstand the old + # behavior to have been. + return pyglet.font.load(font_list).name + + raise ValueError(f"Couldn't find a font for {font_name!r}") def _draw_pyglet_label(label: pyglet.text.Label) -> None: @@ -132,7 +135,8 @@ class Text: :param width: A width limit in pixels :param align: Horizontal alignment; values other than "left" require width to be set :param Union[str, tuple[str, ...]] font_name: A font name, path to a font file, or list of names - :param bold: Whether to draw the text as bold + :param bold: Whether to draw the text as bold, and if a string, + how bold. See :py:attr:`.bold` to learn more. :param italic: Whether to draw the text as italic :param anchor_x: How to calculate the anchor point's x coordinate. Options: "left", "center", or "right" @@ -181,7 +185,7 @@ def __init__( width: int | None = None, align: str = "left", font_name: FontNameOrNames = ("calibri", "arial"), - bold: bool = False, + bold: bool | str = False, italic: bool = False, anchor_x: str = "left", anchor_y: str = "baseline", @@ -204,6 +208,7 @@ def __init__( ) adjusted_font = _attempt_font_name_resolution(font_name) + self._label = pyglet.text.Label( text=text, # pyglet is lying about what it takes here and float is entirely valid @@ -211,13 +216,14 @@ def __init__( y=y, # type: ignore z=z, # type: ignore font_name=adjusted_font, - font_size=font_size, + # TODO: Fix this upstream (Mac & Linux seem to allow float) + font_size=font_size, # type: ignore # use type: ignore since cast is slow & pyglet used Literal anchor_x=anchor_x, # type: ignore anchor_y=anchor_y, # type: ignore color=Color.from_iterable(color), width=width, - align=align, + align=align, # type: ignore bold=bold, italic=italic, multiline=multiline, @@ -330,11 +336,17 @@ def font_name(self) -> FontNameOrNames: """ Get or set the font name(s) for the label """ - return self._label.font_name + if not isinstance(self._label.font_name, str): + return tuple(self._label.font_name) + else: + return self._label.font_name @font_name.setter def font_name(self, font_name: FontNameOrNames) -> None: - self._label.font_name = font_name + if isinstance(font_name, str): + self._label.font_name = font_name + else: + self._label.font_name = list(font_name) @property def font_size(self) -> float: @@ -386,7 +398,7 @@ def color(self) -> Color: """ Get or set the text color for the label """ - return self._label.color + return Color.from_iterable(self._label.color) @color.setter def color(self, color: RGBOrA255): @@ -485,18 +497,27 @@ def align(self, align: str): self._label.set_style("align", align) @property - def bold(self) -> bool: + def bold(self) -> bool | str: """ - Get or set bold state of the label + Get or set bold state of the label. + + The supported values include: + + * ``"black"`` + * ``"bold" (same as ``True``) + * ``"semibold"`` + * ``"semilight"`` + * ``"light"`` + """ return self._label.bold @bold.setter - def bold(self, bold: bool): + def bold(self, bold: bool | str): self._label.bold = bold @property - def italic(self) -> bool: + def italic(self) -> bool | str: """ Get or set the italic state of the label """ @@ -588,7 +609,7 @@ def create_text_sprite( width: int | None = None, align: str = "left", font_name: FontNameOrNames = ("calibri", "arial"), - bold: bool = False, + bold: bool | str = False, italic: bool = False, anchor_x: str = "left", multiline: bool = False, @@ -679,7 +700,7 @@ def draw_text( width: int | None = None, align: str = "left", font_name: FontNameOrNames = ("calibri", "arial"), - bold: bool = False, + bold: bool | str = False, italic: bool = False, anchor_x: str = "left", anchor_y: str = "baseline", @@ -719,7 +740,8 @@ def draw_text( :param width: A width limit in pixels :param align: Horizontal alignment; values other than "left" require width to be set :param Union[str, tuple[str, ...]] font_name: A font name, path to a font file, or list of names - :param bold: Whether to draw the text as bold + :param bold: Whether to draw the text as bold, and if a :py:class:`str`, + how bold to draw it. See :py:attr:`.Text.bold` to learn more. :param italic: Whether to draw the text as italic :param anchor_x: How to calculate the anchor point's x coordinate :param anchor_y: How to calculate the anchor point's y coordinate diff --git a/doc/programming_guide/headless.rst b/doc/programming_guide/headless.rst index 82f19648a..471e57528 100644 --- a/doc/programming_guide/headless.rst +++ b/doc/programming_guide/headless.rst @@ -32,7 +32,11 @@ This can be done in the following ways: import os os.environ["ARCADE_HEADLESS"] = "True" -This means you can configure headless externally. + # The above is a shortcut for + import pyglet + pyglet.options.headless = True + +This of course also means you can configure headless externally. .. code:: bash @@ -178,10 +182,10 @@ to a physical device (graphics card) or a virtual card/device. .. code:: py # Default setting - pyglet.options['headless_device'] = 0 + pyglet.options.headless_device = 0 # Use the second gpu/device - pyglet.options['headless_device'] = 1 + pyglet.options.headless_device = 1 Issues? ------- diff --git a/pyproject.toml b/pyproject.toml index 752e518c4..8386e0e03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,11 @@ classifiers = [ "Topic :: Software Development :: Libraries :: Python Modules", ] dependencies = [ - "pyglet == 2.1dev2", + # Fallback to use pyglet's development branch as a rolling release package + # at the cost of slow download and constant pip install -I -e .[dev] + # "pyglet@git+https://github.com/pyglet/pyglet.git@development#egg=pyglet", + # Expected future dev preview release on PyPI (not yet released) + 'pyglet==2.1.dev4', "pillow~=10.2.0", "pymunk~=6.6.0", "pytiled-parser~=2.2.5", @@ -38,14 +42,14 @@ Book = "https://learn.arcade.academy" # Used for dev work dev = [ # --- Documentation: Sphinx 7 based currently - "sphinx==7.3.7", # April 2024 | Updated 2024-07-15, 7.4+ is broken with sphinx-autobuild - "sphinx_rtd_theme==2.0.0", # Nov 2023 + "sphinx==7.3.7", # April 2024 | Updated 2024-07-15, 7.4+ is broken with sphinx-autobuild + "sphinx_rtd_theme==2.0.0", # Nov 2023 "sphinx-rtd-dark-mode==1.3.0", - "sphinx-autobuild==2024.4.16", # April 2024 | Due to this, Python 3.10+ is required to serve docs - "sphinx-copybutton==0.5.2", # April 2023 - "sphinx-sitemap==2.6.0", # April 2024 - "pygments==2.17.2", # 2.18 has breaking changes in lexer - "docutils==0.20.1", # ? + "sphinx-autobuild==2024.4.16", # April 2024 | Due to this, Python 3.10+ is required to serve docs + "sphinx-copybutton==0.5.2", # April 2023 + "sphinx-sitemap==2.6.0", # April 2024 + "pygments==2.17.2", # 2.18 has breaking changes in lexer + "docutils==0.20.1", # ? # "pyyaml==6.0.1", # "readthedocs-sphinx-search==0.3.2", # "sphinx-autodoc-typehints==2.0.1", @@ -54,12 +58,12 @@ dev = [ "pytest-cov", "pytest-mock", "coverage", - "coveralls", # Do we really need this? + "coveralls", # Do we really need this? "black", "ruff", "mypy", - "pyright==1.1.355", - "typer[all]==0.11.0", # Needed for make.py + "pyright==1.1.372", + "typer[all]==0.11.0", # Needed for make.py "wheel", ] # Testing only diff --git a/tests/__init__.py b/tests/__init__.py index ca0a59529..40bd75a95 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -3,4 +3,4 @@ # Headless mode if os.environ.get("ARCADE_HEADLESS_TEST"): import pyglet - pyglet.options["headless"] = True + pyglet.options.headless = True diff --git a/tests/unit/test_arcade.py b/tests/unit/test_arcade.py index e64c08291..56e05ac4a 100644 --- a/tests/unit/test_arcade.py +++ b/tests/unit/test_arcade.py @@ -5,6 +5,10 @@ from arcade import * +# TODO: double-check whether this is actually the right solution? +builtin_types = frozenset((bool, str, int, ModuleType)) + + def test_import(): """Compare arcade.__all__ to the actual module contents""" import arcade @@ -16,7 +20,8 @@ def test_import(): remaining = arcade_names - common for name in copy(remaining): attr = getattr(arcade, name) - if type(attr) is ModuleType: + attr_type = type(attr) + if attr_type in builtin_types: remaining.remove(name) elif not attr.__module__.startswith('arcade.'): remaining.remove(name)