From b759a263af4d7863aef7ac74ad92463948750db0 Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 29 Mar 2025 12:17:39 +0100 Subject: [PATCH 1/4] All output from docs build should use logging --- util/create_resources_listing.py | 39 ++++++++++++++++------------- util/doc_helpers/real_filesystem.py | 6 ++--- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/util/create_resources_listing.py b/util/create_resources_listing.py index 8213a0cde2..150b9cb072 100644 --- a/util/create_resources_listing.py +++ b/util/create_resources_listing.py @@ -266,13 +266,13 @@ def do_heading( ref_target: ``True`` to auto-generate it or a str to use a specific one. """ out.write("\n") - print(f"doing heading: {heading_text!r} {relative_heading_level}") + log.info(f"doing heading: {heading_text!r} {relative_heading_level}") num_headings = len(headings_lookup) if ref_target is True: ref_target = f"resources-{heading_text}.rst" if ref_target: - print(f" writing ref target {repr(heading_text)}") + log.info(f" writing ref target {repr(heading_text)}") out.write(f".. _{ref_target.lower()}:\n\n") if relative_heading_level >= num_headings: @@ -389,9 +389,9 @@ def process_resource_directory(out, dir: Path): file_list = filter_dir(path, keep=is_unskipped_file) num_files = len(file_list) if num_files <= 0: - print(f" SKIP: No files... {num_files}") + log.info(f" SKIP: No files... {num_files}") else: - print(" HAS FILES!") + log.info(" HAS FILES!") handle_raw = path_as_resource_handle(path, suffix="/") config: HandleLevelConfigDict = RESOURCE_HANDLE_CONFIGS.get(handle_raw, {}) resource_handle = handle_raw.removesuffix('./') @@ -408,28 +408,31 @@ def process_resource_directory(out, dir: Path): handle_steps_wholes.append( f"{handle_steps_wholes[-1]}{handle_step_whole}/") - print(" Subdir Config:") + log.info(" Subdir Config:") _l = locals() for k in filter(lambda _k: 'handle' in _k and('steps' in _k or _k.count('_') <2), _l.keys()): - print(f" {k} : {_l.get(k, None)!r}" if k else '') + log.info(f" {k} : {_l.get(k, None)!r}" if k else '') # Process headings and render any new ones we haven't seen for heading_level, handle_step_whole in enumerate(handle_steps_wholes, start=0): - print(" heading check", (heading_level, handle_step_whole)) + log.info(" heading check", (heading_level, handle_step_whole)) if handle_step_whole in SKIP_HANDLES: - print(" skipping excluded") + log.info(" skipping excluded") continue if handle_step_whole in visited_headings: - print(" skipping visited") + log.info(" skipping visited") continue visited_headings.add(handle_step_whole) local_config = RESOURCE_HANDLE_CONFIGS.get(handle_step_whole, {}) local_heading_config = local_config.get('heading', {}) - print("proceeding...", - "\n config ", local_config, - "\n heading_config ", local_heading_config, sep = "") + log.info( + ("proceeding... " + f"\n config {local_config}", + f"\n heading_config {local_heading_config}" + ) + ) # Heading config fetch and write use_level = local_heading_config.get('level', heading_level) @@ -442,9 +445,9 @@ def process_resource_directory(out, dir: Path): for k, v in locals().items(): if k.startswith("use_"): - print(repr(k), ":", repr(v)) + log.info("%s : %s", repr(k), repr(v)) - print(f" got target: {use_target!r}") + log.info(f" got target: {use_target!r}") do_heading(out, use_level, use_value, ref_target=use_target) out.write(f"\n.. comment `{handle_step_whole!r}``\n\n") @@ -548,7 +551,7 @@ def from_path(cls, path: Path) -> Self: face_name_pieces = (face_name_parts.get("face_name") or '').split('_') raw_name = ' '.join(face_name_pieces) - print(face_name_parts) + log.info(face_name_parts) styles = tuple(BRITTLE_CAP_WORD_REGEX.findall( face_name_parts.get('styles', None) or '')) @@ -635,11 +638,11 @@ def do_filetile(out, suffix: str | None = None, state: str = None): p = FILETILE_DIR / f"type-{suffix.strip('.')}.png" log.info(f" FILETILE: {p}") if p.exists(): - print(f" KNOWN! {p.name!r}") + log.info(f" KNOWN! {p.name!r}") name = p.name else: name = f"type-unknown.png" - print(" ... unknown :(") + log.info(" ... unknown :(") else: name = "state-error.png" out.write(indent(f" ", @@ -895,7 +898,7 @@ def resources(): process_resource_directory(out, RESOURCE_DIR) out.close() - print("Done creating resources.rst") + log.info("Done creating resources.rst") vfs = Vfs() diff --git a/util/doc_helpers/real_filesystem.py b/util/doc_helpers/real_filesystem.py index 69ff96beb3..0cfc635be2 100644 --- a/util/doc_helpers/real_filesystem.py +++ b/util/doc_helpers/real_filesystem.py @@ -109,10 +109,10 @@ def copy_media( done = set() logging.info("") for dir_name, sub_items in items.items(): - print(f" Copying... {' '.join(map(repr, sub_items))}...") + log.info(f" Copying... {' '.join(map(repr, sub_items))}...") src_sub = (src_root / dir_name).resolve() dest_sub = dest_root / dir_name - print(" from :", src_sub) - print(" to :", dest_sub) + log.info(" from :", src_sub) + log.info(" to :", dest_sub) sync_dir(src_sub, dest_sub, *items, done=done) From 83a64bc2e61a738e36a82a0a890c3ad802831a72 Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 29 Mar 2025 12:18:26 +0100 Subject: [PATCH 2/4] Make warning the default log level --- doc/conf.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 82b8bd05d5..a7c5da0f5c 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -18,7 +18,8 @@ UTIL_DIR = REPO_LOCAL_ROOT / "util" log = logging.getLogger('conf.py') -logging.basicConfig(level=logging.INFO) +logging.basicConfig(level=logging.WARNING) +# logging.basicConfig(level=logging.INFO) sys.path.insert(0, str(REPO_LOCAL_ROOT)) sys.path.insert(0, str(ARCADE_MODULE)) @@ -513,12 +514,12 @@ def run(self) -> tuple[list[nodes.Node], list[nodes.system_message]]: def setup(app): - print("Diagnostic info since readthedocs doesn't use our make.py:") + log.info("Diagnostic info since readthedocs doesn't use our make.py:") for attr, comment in APP_CONFIG_DIRS: val = getattr(app, attr, None) - print(f" {attr}: {val!r}") + log.info(f"{attr}: {val!r}") if comment: - print(f" {comment}") + log.info(f" {comment}") # Separate stylesheets loosely by category. # pending: sphinx >= 8.1.4 to remove the sphinx_static_file_temp_fix.py From f89e4fbeb7d5ab88438dfe0c17ad43219b7f4420 Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 29 Mar 2025 12:18:41 +0100 Subject: [PATCH 3/4] Missing docstring in pymunk_physics_engine --- arcade/pymunk_physics_engine.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/arcade/pymunk_physics_engine.py b/arcade/pymunk_physics_engine.py index 8d47d7453a..da5199a2a4 100644 --- a/arcade/pymunk_physics_engine.py +++ b/arcade/pymunk_physics_engine.py @@ -499,6 +499,15 @@ def set_position(self, sprite: Sprite, position: pymunk.Vec2d | tuple[float, flo physics_object.body.position = position def set_rotation(self, sprite: Sprite, rotation: float) -> None: + """ + Set the rotation of the sprite + + Args: + sprite: + A sprite known to the physics engine. + rotation: + The angle in degrees (clockwise). + """ physics_object = self.get_physics_object(sprite) if physics_object.body is None: raise PymunkException( From e4a9706e077dde46df1a87960560bc6c61812f4e Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 29 Mar 2025 12:58:15 +0100 Subject: [PATCH 4/4] Missing docstrings --- arcade/camera/data_types.py | 11 ++++- arcade/clock.py | 3 ++ arcade/easing.py | 3 ++ .../future/background/background_texture.py | 42 +++++++++++++++++++ arcade/future/light/lights.py | 5 +++ arcade/gl/types.py | 12 ++++++ arcade/gl/uniform.py | 7 +++- arcade/isometric.py | 33 +++++++++++++++ 8 files changed, 113 insertions(+), 3 deletions(-) diff --git a/arcade/camera/data_types.py b/arcade/camera/data_types.py index c67b90c32c..50c6704d6d 100644 --- a/arcade/camera/data_types.py +++ b/arcade/camera/data_types.py @@ -427,7 +427,16 @@ def use(self) -> None: ... @contextmanager - def activate(self) -> Generator[Self, None, None]: ... + def activate(self) -> Generator[Self, None, None]: + """ + Activate this projector for rendering. + + This is a context manager and should be used with a ``with`` statement:: + + with projector.activate(): + # Render with this projector + """ + ... def project(self, world_coordinate: Point) -> Vec2: """ diff --git a/arcade/clock.py b/arcade/clock.py index 1ebb20e597..047c95bbfc 100644 --- a/arcade/clock.py +++ b/arcade/clock.py @@ -95,6 +95,9 @@ def ticks_since(self, tick: int) -> int: @property def max_deltatime(self) -> float | None: + """ + The maximum deltatime that the clock will allow. If a large dt is passed into + """ return self._max_deltatime @property diff --git a/arcade/easing.py b/arcade/easing.py index 2ce734dee9..a88dfb4900 100644 --- a/arcade/easing.py +++ b/arcade/easing.py @@ -23,6 +23,9 @@ class EasingData: ease_function: Callable def reset(self) -> None: + """ + Reset the easing data to its initial state. + """ self.cur_period = self.start_period diff --git a/arcade/future/background/background_texture.py b/arcade/future/background/background_texture.py index ec15360a9a..be8dbbd0e9 100644 --- a/arcade/future/background/background_texture.py +++ b/arcade/future/background/background_texture.py @@ -15,6 +15,12 @@ class BackgroundTexture: The Mat3s define the scaling, rotation, and translation of the pixel data in the texture. see background_fs.glsl in resources/shaders for an implementation of this. + + Args: + texture: The texture to use as the background. + offset: The offset of the texture in pixels. + scale: The scale of the texture. + angle: The angle of the texture in radians. """ def __init__( @@ -41,6 +47,10 @@ def pixel_transform(self): @property def scale(self) -> float: + """ + Get or set the scale of the texture. This is a multiplier on the size of the texture. + Default value is ``1.0``. + """ return self._scale @scale.setter @@ -50,6 +60,10 @@ def scale(self, value: float): @property def angle(self) -> float: + """ + Get or set the angle of the texture. This is a rotation in radians. + Default value is ``0.0``. + """ return self._angle @angle.setter @@ -59,6 +73,10 @@ def angle(self, value: float): @property def offset(self) -> tuple[float, float]: + """ + Get or set the offset of the texture. This is a translation in pixels. + Default value is ``(0.0, 0.0)``. + """ return self._offset @offset.setter @@ -134,6 +152,17 @@ def render_target( color_attachments: list[gl.Texture2D] | None = None, depth_attachment: gl.Texture2D | None = None, ) -> gl.Framebuffer: + """ + Create a framebuffer for the texture. + + This framebuffer is used to render to the texture. The framebuffer is created with the + texture as the color attachment. + + Args: + context: The context to use for the framebuffer. + color_attachments: The color attachments to use for the framebuffer." + depth_attachment: The depth attachment to use for the framebuffer." + """ if color_attachments is None: color_attachments = [] return context.framebuffer( @@ -149,6 +178,19 @@ def from_file( angle: float = 0.0, filters=(gl.NEAREST, gl.NEAREST), ): + """ " + Create a BackgroundTexture from a file. + This is a convenience function to create a BackgroundTexture from a file. + + The file is loaded using PIL and converted to a texture. + + Args: + tex_src: The file to load. + offset: The offset of the texture in pixels. + scale: The scale of the texture. + angle: The angle of the texture in radians. + filters: The filters to use for the texture. + """ _context = get_window().ctx with Image.open(resolve(tex_src)).convert("RGBA") as img: diff --git a/arcade/future/light/lights.py b/arcade/future/light/lights.py index 42fb51e45e..243cb51570 100644 --- a/arcade/future/light/lights.py +++ b/arcade/future/light/lights.py @@ -122,19 +122,23 @@ def __init__(self, width: int, height: int): @property def diffuse_texture(self): + """The diffuse texture""" return self.texture @property def light_texture(self): + """The light texture""" return self._light_buffer.color_attachments[0] def resize(self, width, height): + """Resize the light layer""" super().resize(width, height) self._light_buffer = self.ctx.framebuffer( color_attachments=self.ctx.texture((width, height), components=3) ) def clear(self): + """Clear the light layer""" super().clear() self._light_buffer.clear() @@ -145,6 +149,7 @@ def add(self, light: Light): self._rebuild = True def extend(self, lights: Sequence[Light]): + """Add a list of lights to the layer""" for light in lights: self.add(light) diff --git a/arcade/gl/types.py b/arcade/gl/types.py index 1c7e4589fc..2af7fcbd64 100644 --- a/arcade/gl/types.py +++ b/arcade/gl/types.py @@ -192,11 +192,17 @@ def __init__( location=0, ): self.name = name + """The name of the attribute in the program""" self.gl_type = gl_type + """The OpenGL type of the attribute""" self.components = components + """Number of components for this attribute (1, 2, 3 or 4)""" self.bytes_per_component = bytes_per_component + """How many bytes for a single component""" self.offset = offset + """Offset of the attribute in the buffer""" self.location = location + """Location of the attribute in the program""" @property def bytes_total(self) -> int: @@ -408,13 +414,19 @@ def __init__( self, name: str, enum: GLenumLike, gl_type: PyGLenum, gl_size: int, components: int ): self.name = name + """The string representation of this type""" self.enum = enum + """The OpenEL enum of this type""" self.gl_type = gl_type + """The base OpenGL data type""" self.gl_size = gl_size + """The size of the base OpenGL data type""" self.components = components + """The number of components (1, 2, 3 or 4)""" @property def size(self) -> int: + """The total size of this type in bytes""" return self.gl_size * self.components def __repr__(self) -> str: diff --git a/arcade/gl/uniform.py b/arcade/gl/uniform.py index bf4584e920..87d9e3a264 100644 --- a/arcade/gl/uniform.py +++ b/arcade/gl/uniform.py @@ -1,5 +1,6 @@ import struct from ctypes import POINTER, c_double, c_float, c_int, c_uint, cast +from typing import Callable from pyglet import gl @@ -173,8 +174,10 @@ def __init__(self, ctx, program_id, location, name, data_type, array_length): self._array_length = array_length # Number of components (including per array entry) self._components = 0 - #: The getter function configured for this uniform - #: The setter function configured for this uniform + self.getter: Callable + """The getter function configured for this uniform""" + self.setter: Callable + """The setter function configured for this uniform""" self._setup_getters_and_setters() @property diff --git a/arcade/isometric.py b/arcade/isometric.py index e2166d1214..55c718d4a2 100644 --- a/arcade/isometric.py +++ b/arcade/isometric.py @@ -5,6 +5,17 @@ def isometric_grid_to_screen( tile_x: int, tile_y: int, width: int, height: int, tile_width: int, tile_height: int ) -> tuple[int, int]: + """ + Convert isometric grid coordinates to screen coordinates. + + Args: + tile_x: The x coordinate of the tile in the isometric grid. + tile_y: The y coordinate of the tile in the isometric grid. + width: The width of the screen. + height: The height of the screen. + tile_width: The width of a tile in pixels. + tile_height: The height of a tile in pixels. + """ screen_x = tile_width * tile_x // 2 + height * tile_width // 2 - tile_y * tile_width // 2 screen_y = ( (height - tile_y - 1) * tile_height // 2 @@ -17,6 +28,17 @@ def isometric_grid_to_screen( def screen_to_isometric_grid( screen_x: int, screen_y: int, width: int, height: int, tile_width: int, tile_height: int ) -> tuple[int, int]: + """ + Convert screen coordinates to isometric grid coordinates. + + Args: + screen_x: The x coordinate on the screen. + screen_y: The y coordinate on the screen. + width: The width of the screen. + height: The height of the screen. + tile_width: The width of a tile in pixels. + tile_height: The height of a tile in pixels. + """ x2 = (1 / tile_width * screen_x / 2 - 1 / tile_height * screen_y / 2 + width / 2) * 2 - ( width / 2 + 0.5 ) @@ -31,6 +53,17 @@ def screen_to_isometric_grid( def create_isometric_grid_lines( width: int, height: int, tile_width: int, tile_height: int, color: RGBA255, line_width: int ) -> ShapeElementList: + """ + Create a ShapeElementList of isometric grid lines. + + Args: + width: The width of the grid in tiles. + height: The height of the grid in tiles. + tile_width: The width of a tile in pixels. + tile_height: The height of a tile in pixels. + color: The color of the lines. + line_width: The width of the lines. + """ # Grid lines 1 shape_list: ShapeElementList = ShapeElementList()