diff --git a/moderngl_window/__init__.py b/moderngl_window/__init__.py index 5ac1f55..ab5b74c 100644 --- a/moderngl_window/__init__.py +++ b/moderngl_window/__init__.py @@ -16,8 +16,7 @@ from moderngl_window.conf import settings from moderngl_window.context.base import BaseWindow, WindowConfig from moderngl_window.timers.clock import Timer -from moderngl_window.utils.keymaps import (AZERTY, QWERTY, KeyMap, # noqa - KeyMapFactory) +from moderngl_window.utils.keymaps import AZERTY, QWERTY, KeyMap, KeyMapFactory # noqa from moderngl_window.utils.module_loading import import_string __version__ = "3.0.0" @@ -74,7 +73,9 @@ class ContextRefs: CONTEXT: Optional[moderngl.Context] = None -def activate_context(window: Optional[BaseWindow] = None, ctx: Optional[moderngl.Context] = None) -> None: +def activate_context( + window: Optional[BaseWindow] = None, ctx: Optional[moderngl.Context] = None +) -> None: """ Register the active window and context. If only a window is supplied the context is taken from the window. @@ -122,9 +123,12 @@ def get_window_cls(window: str = "") -> type[BaseWindow]: logger.info("Attempting to load window class: %s", window) win = import_string(window) - assert issubclass(win, BaseWindow), f"{win} is not derived from moderngl_window.context.base.BaseWindow" + assert issubclass( + win, BaseWindow + ), f"{win} is not derived from moderngl_window.context.base.BaseWindow" return win + def get_local_window_cls(window: Optional[str] = None) -> type[BaseWindow]: """ Attempt to obtain a window class in the moderngl_window package @@ -172,7 +176,9 @@ def create_window_from_settings() -> BaseWindow: window_cls = import_string(settings.WINDOW["class"]) window = window_cls(**settings.WINDOW) - assert isinstance(window, BaseWindow), f"{type(window)} is not derived from moderngl_window.context.base.BaseWindow" + assert isinstance( + window, BaseWindow + ), f"{type(window)} is not derived from moderngl_window.context.base.BaseWindow" activate_context(window=window) return window @@ -180,7 +186,9 @@ def create_window_from_settings() -> BaseWindow: # --- The simple window config system --- -def run_window_config(config_cls: type[WindowConfig], timer: Optional[Timer] = None, args: Any = None) -> None: +def run_window_config( + config_cls: type[WindowConfig], timer: Optional[Timer] = None, args: Any = None +) -> None: """ Run an WindowConfig entering a blocking main loop @@ -322,7 +330,9 @@ def create_parser() -> argparse.ArgumentParser: return parser -def parse_args(args: Optional[Any] = None, parser: Optional[argparse.ArgumentParser] = None) -> argparse.Namespace: +def parse_args( + args: Optional[Any] = None, parser: Optional[argparse.ArgumentParser] = None +) -> argparse.Namespace: """Parse arguments from sys.argv Passing in your own argparser can be user to extend the parser. diff --git a/moderngl_window/capture/base.py b/moderngl_window/capture/base.py index 3839925..2036921 100644 --- a/moderngl_window/capture/base.py +++ b/moderngl_window/capture/base.py @@ -77,7 +77,9 @@ def _remove_file(self) -> None: if os.path.exists(self._filename): os.remove(self._filename) - def start_capture(self, filename: Optional[str] = None, framerate: Union[int, float] = 60) -> None: + def start_capture( + self, filename: Optional[str] = None, framerate: Union[int, float] = 60 + ) -> None: """ Start the capturing process @@ -132,7 +134,7 @@ def save(self) -> None: """ if not self._recording: return - + if self._source is None: return diff --git a/moderngl_window/context/base/window.py b/moderngl_window/context/base/window.py index e053198..5565966 100644 --- a/moderngl_window/context/base/window.py +++ b/moderngl_window/context/base/window.py @@ -12,8 +12,12 @@ from moderngl_window.context.base import BaseKeys, KeyModifiers from moderngl_window.geometry.attributes import AttributeNames from moderngl_window.loaders.texture.icon import IconLoader -from moderngl_window.meta import (DataDescription, ProgramDescription, - SceneDescription, TextureDescription) +from moderngl_window.meta import ( + DataDescription, + ProgramDescription, + SceneDescription, + TextureDescription, +) from moderngl_window.scene import Scene from moderngl_window.timers.base import BaseTimer @@ -82,10 +86,10 @@ class BaseWindow: def __init__( self, - title: str="ModernGL", + title: str = "ModernGL", gl_version: tuple[int, int] = (3, 3), size: tuple[int, int] = (1280, 720), - resizable: bool= True, + resizable: bool = True, visible: bool = True, fullscreen: bool = False, vsync: bool = True, @@ -140,7 +144,9 @@ def __init__( self._mouse_drag_event_func: Callable[[int, int, int, int], None] = dummy_func self._mouse_scroll_event_func: Callable[[float, float], None] = dummy_func self._unicode_char_entered_func: Callable[[str], None] = dummy_func - self._files_dropped_event_func: Callable[[int, int, list[Union[str, Path]]], None] = dummy_func + self._files_dropped_event_func: Callable[[int, int, list[Union[str, Path]]], None] = ( + dummy_func + ) self._on_generic_event_func: Callable[[Event], None] = dummy_func # Internal states @@ -555,7 +561,9 @@ def files_dropped_event_func(self) -> Callable[[int, int, list[Union[str, Path]] @files_dropped_event_func.setter @require_callable - def files_dropped_event_func(self, func: Callable[[int, int, list[Union[str, Path]]], None]) -> None: + def files_dropped_event_func( + self, func: Callable[[int, int, list[Union[str, Path]]], None] + ) -> None: self._files_dropped_event_func = func @property @@ -585,7 +593,7 @@ def mouse_position_event_func(self) -> Callable[[int, int, int, int], None]: @mouse_position_event_func.setter @require_callable - def mouse_position_event_func(self, func:Callable[[int, int, int, int], None]) -> None: + def mouse_position_event_func(self, func: Callable[[int, int, int, int], None]) -> None: self._mouse_position_event_func = func @property @@ -673,7 +681,9 @@ def _handle_mouse_button_state_change(self, button: int, pressed: bool) -> None: else: raise ValueError("Incompatible mouse button number: {}".format(button)) - def convert_window_coordinates(self, x: int, y: int, x_flipped: bool = False, y_flipped: bool = False) -> tuple[int, int]: + def convert_window_coordinates( + self, x: int, y: int, x_flipped: bool = False, y_flipped: bool = False + ) -> tuple[int, int]: """ Convert window coordinates to top-left coordinate space. The default origin is the top left corner of the window. @@ -716,7 +726,15 @@ def use(self) -> None: """Bind the window's framebuffer""" self._ctx.screen.use() - def clear(self, red: float =0.0, green: float=0.0, blue: float=0.0, alpha: float=0.0, depth: float =1.0, viewport: Optional[tuple[int, int, int, int]]=None) -> None: + def clear( + self, + red: float = 0.0, + green: float = 0.0, + blue: float = 0.0, + alpha: float = 0.0, + depth: float = 1.0, + viewport: Optional[tuple[int, int, int, int]] = None, + ) -> None: """ Binds and clears the default framebuffer @@ -866,7 +884,9 @@ def _calc_mouse_delta(self, xpos: int, ypos: int) -> tuple[int, int]: return dx, dy @property - def on_generic_event_func(self) -> Union[Callable[[int, int, int, int], None], Callable[[Event], None]]: + def on_generic_event_func( + self, + ) -> Union[Callable[[int, int, int, int], None], Callable[[Event], None]]: """ callable: Get or set the on_generic_event callable used to funnel all non-processed events @@ -1284,7 +1304,9 @@ def load_texture_2d( **kwargs, ) ) - assert isinstance(texture, moderngl.Texture), f"There was an error when loading the texture, {type(texture)} is not a moderngl.Texture" + assert isinstance( + texture, moderngl.Texture + ), f"There was an error when loading the texture, {type(texture)} is not a moderngl.Texture" return texture def load_texture_array( @@ -1335,7 +1357,9 @@ def load_texture_array( **kwargs, ) ) - assert isinstance(texture, moderngl.TextureArray), f"There was an error when loading the texture, {type(texture)} is not a moderngl.TextureArray" + assert isinstance( + texture, moderngl.TextureArray + ), f"Error when loading the texture, {type(texture)} is not a moderngl.TextureArray" return texture def load_texture_cube( @@ -1346,7 +1370,7 @@ def load_texture_cube( neg_x: str = "", neg_y: str = "", neg_z: str = "", - flip : bool = False, + flip: bool = False, flip_x: bool = False, flip_y: bool = False, mipmap: bool = False, @@ -1398,7 +1422,9 @@ def load_texture_cube( ) ) - assert isinstance(texture, moderngl.TextureCube), f"There was an error when loading the texture, {type(texture)} is not a moderngl.TextureCube" + assert isinstance( + texture, moderngl.TextureCube + ), f"Error when loading the texture. {type(texture)} is not a moderngl.TextureCube" return texture def load_program( @@ -1447,7 +1473,9 @@ def load_program( ) ) - assert isinstance(prog, moderngl.Program), f"There was an error when loading the program, {type(prog)} is not a moderngl.Program" + assert isinstance( + prog, moderngl.Program + ), f"There was an error when loading the program, {type(prog)} is not a moderngl.Program" return prog def load_compute_shader( @@ -1466,7 +1494,9 @@ def load_compute_shader( ProgramDescription(compute_shader=path, defines=defines, **kwargs) ) - assert isinstance(shader, moderngl.ComputeShader), f"There was an error when loading the shader, {type(shader)} is not a moderngl.ComputeShader" + assert isinstance( + shader, moderngl.ComputeShader + ), f"Error loading compute shader. {type(shader)} is not a moderngl.ComputeShader" return shader def load_text(self, path: str, **kwargs: Any) -> str: @@ -1490,7 +1520,9 @@ def load_text(self, path: str, **kwargs: Any) -> str: text = resources.data.load(DataDescription(path=path, **kwargs)) - assert isinstance(text, str), f"There was an error when loading the text, {type(text)} is not a string" + assert isinstance( + text, str + ), f"There was an error when loading the text, {type(text)} is not a string" return text def load_json(self, path: str, **kwargs: Any) -> dict[str, Any]: @@ -1514,7 +1546,9 @@ def load_json(self, path: str, **kwargs: Any) -> dict[str, Any]: json = resources.data.load(DataDescription(path=path, **kwargs)) - assert isinstance(json, dict), f"There was an error when loading the Texture, {type(json)} is not a dictionnary" + assert isinstance( + json, dict + ), f"There was an error when loading the Texture, {type(json)} is not a dictionnary" return json def load_binary(self, path: str, **kwargs: Any) -> bytes: @@ -1538,11 +1572,18 @@ def load_binary(self, path: str, **kwargs: Any) -> bytes: binary = resources.data.load(DataDescription(path=path, kind="binary")) - assert isinstance(binary, bytes), f"There was an error when loading the binary, {type(binary)} is not a binary file" + assert isinstance( + binary, bytes + ), f"There was an error when loading the binary, {type(binary)} is not a binary file" return binary def load_scene( - self, path: str, cache: bool = False, attr_names: type[AttributeNames] = AttributeNames, kind: Optional[str] = None, **kwargs: Any + self, + path: str, + cache: bool = False, + attr_names: type[AttributeNames] = AttributeNames, + kind: Optional[str] = None, + **kwargs: Any, ) -> Scene: """Loads a scene. @@ -1569,7 +1610,9 @@ def load_scene( ) ) - assert isinstance(scene, Scene), f"There was an error when loading the scene, {type(scene)} is not a Scene" + assert isinstance( + scene, Scene + ), f"There was an error when loading the scene, {type(scene)} is not a Scene" return scene diff --git a/moderngl_window/context/glfw/window.py b/moderngl_window/context/glfw/window.py index 97cab29..0fb34d0 100644 --- a/moderngl_window/context/glfw/window.py +++ b/moderngl_window/context/glfw/window.py @@ -266,7 +266,9 @@ def _set_icon(self, icon_path: Path) -> None: image = Image.open(icon_path) glfw.set_window_icon(self._window, 1, image) - def glfw_key_event_callback(self, window: Any, key: GLFW_key, scancode: int, action: GLFW_key, mods: GLFW_key) -> None: + def glfw_key_event_callback( + self, window: Any, key: GLFW_key, scancode: int, action: GLFW_key, mods: GLFW_key + ) -> None: """Key event callback for glfw. Translates and forwards keyboard event to :py:func:`keyboard_event` @@ -311,7 +313,9 @@ def glfw_mouse_event_callback(self, window: Any, xpos: float, ypos: float) -> No else: self._mouse_position_event_func(xpos, ypos, dx, dy) - def glfw_mouse_button_callback(self, window: Any, button: GLFW_key, action: GLFW_key, mods: GLFW_key) -> None: + def glfw_mouse_button_callback( + self, window: Any, button: GLFW_key, action: GLFW_key, mods: GLFW_key + ) -> None: """Handle mouse button events and forward them to the example Args: diff --git a/moderngl_window/context/headless/window.py b/moderngl_window/context/headless/window.py index 3a9d312..0c6fd02 100644 --- a/moderngl_window/context/headless/window.py +++ b/moderngl_window/context/headless/window.py @@ -84,7 +84,15 @@ def use(self) -> None: assert self._fbo is not None, "No framebuffer defined, did you forget to call create_fbo()?" self._fbo.use() - def clear(self, red: float = 0.0, green: float = 0.0, blue: float = 0.0, alpha: float = 0.0, depth: float = 1.0, viewport: Optional[tuple[int, int, int, int]] = None) -> None: + def clear( + self, + red: float = 0.0, + green: float = 0.0, + blue: float = 0.0, + alpha: float = 0.0, + depth: float = 1.0, + viewport: Optional[tuple[int, int, int, int]] = None, + ) -> None: """ Binds and clears the default framebuffer diff --git a/moderngl_window/context/pyglet/window.py b/moderngl_window/context/pyglet/window.py index 9acd6f1..4cb47e9 100644 --- a/moderngl_window/context/pyglet/window.py +++ b/moderngl_window/context/pyglet/window.py @@ -8,9 +8,8 @@ pyglet.options["shadow_window"] = False pyglet.options["debug_gl"] = False - -from pathlib import Path -from typing import Any, Union +from pathlib import Path # noqa +from typing import Any, Union # noqa from moderngl_window.context.base import BaseWindow # noqa: E402 from moderngl_window.context.pyglet.keys import Keys # noqa: E402 diff --git a/moderngl_window/finders/base.py b/moderngl_window/finders/base.py index 0979ecd..0669e8c 100644 --- a/moderngl_window/finders/base.py +++ b/moderngl_window/finders/base.py @@ -6,7 +6,7 @@ import logging from collections import namedtuple from pathlib import Path -from typing import Any, Optional +from typing import Optional from moderngl_window.conf import settings from moderngl_window.exceptions import ImproperlyConfigured @@ -72,7 +72,7 @@ def find(self, path: Path) -> Optional[Path]: if abspath.exists(): logger.debug("found %s", abspath) - return Path(abspath) # Needed to please mypy, but is already be a path + return Path(abspath) # Needed to please mypy, but is already be a path return None diff --git a/moderngl_window/geometry/bbox.py b/moderngl_window/geometry/bbox.py index cd9d90e..16ef368 100644 --- a/moderngl_window/geometry/bbox.py +++ b/moderngl_window/geometry/bbox.py @@ -7,7 +7,11 @@ from moderngl_window.opengl.vao import VAO -def bbox(size: tuple[float, float, float] = (1.0, 1.0, 1.0), name: Optional[str] = None, attr_names: type[AttributeNames] = AttributeNames) -> VAO: +def bbox( + size: tuple[float, float, float] = (1.0, 1.0, 1.0), + name: Optional[str] = None, + attr_names: type[AttributeNames] = AttributeNames, +) -> VAO: """ Generates a bounding box with (0.0, 0.0, 0.0) as the center. This is simply a box with ``LINE_STRIP`` as draw mode. diff --git a/moderngl_window/geometry/quad.py b/moderngl_window/geometry/quad.py index 945132b..651ca49 100644 --- a/moderngl_window/geometry/quad.py +++ b/moderngl_window/geometry/quad.py @@ -7,7 +7,12 @@ from moderngl_window.opengl.vao import VAO -def quad_fs(attr_names: type[AttributeNames] = AttributeNames, normals: bool = True, uvs: bool = True, name: Optional[str] = None) -> VAO: +def quad_fs( + attr_names: type[AttributeNames] = AttributeNames, + normals: bool = True, + uvs: bool = True, + name: Optional[str] = None, +) -> VAO: """ Creates a screen aligned quad using two triangles with normals and texture coordinates. diff --git a/moderngl_window/geometry/sphere.py b/moderngl_window/geometry/sphere.py index 5693e74..16cd459 100644 --- a/moderngl_window/geometry/sphere.py +++ b/moderngl_window/geometry/sphere.py @@ -1,5 +1,5 @@ import math -from typing import Any, Optional +from typing import Optional import moderngl as mlg import numpy diff --git a/moderngl_window/loaders/base.py b/moderngl_window/loaders/base.py index 0589342..74988f9 100644 --- a/moderngl_window/loaders/base.py +++ b/moderngl_window/loaders/base.py @@ -115,7 +115,9 @@ def find_scene(self, path: Optional[Union[str, Path]]) -> Optional[Path]: """ return self._find(path, scene.get_finders()) - def _find(self, path: Optional[Union[str, Path]], finders: Iterable[BaseFilesystemFinder]) -> Optional[Path]: + def _find( + self, path: Optional[Union[str, Path]], finders: Iterable[BaseFilesystemFinder] + ) -> Optional[Path]: """Find the first occurrance of this path in all finders. If the incoming path is an absolute path we assume this path exist and return it. diff --git a/moderngl_window/loaders/scene/gltf2.py b/moderngl_window/loaders/scene/gltf2.py index bb62d8b..4278e1d 100644 --- a/moderngl_window/loaders/scene/gltf2.py +++ b/moderngl_window/loaders/scene/gltf2.py @@ -22,7 +22,6 @@ from moderngl_window.loaders.texture import t2d from moderngl_window.meta import SceneDescription, TextureDescription from moderngl_window.opengl.vao import VAO -from moderngl_window.resources.textures import Textures from moderngl_window.scene import Material, MaterialTexture, Mesh, Node, Scene logger = logging.getLogger(__name__) @@ -154,7 +153,9 @@ def load_glb(self) -> None: magic = fd.read(4) if magic != GLTF_MAGIC_HEADER: raise ValueError( - "{} has incorrect header {!r} != {!r}".format(self.path, magic, GLTF_MAGIC_HEADER) + "{} has incorrect header {!r} != {!r}".format( + self.path, magic, GLTF_MAGIC_HEADER + ) ) version = struct.unpack(" Node: class GLTFMeta: """Container for gltf metadata""" - def __init__(self, path: Union[Path, str], data: dict[Any, Any], meta: SceneDescription, binary_buffer: Optional[bytes] = None) -> None: + def __init__( + self, + path: Union[Path, str], + data: dict[Any, Any], + meta: SceneDescription, + binary_buffer: Optional[bytes] = None, + ) -> None: """ :param file: GLTF file name loaded :param data: Metadata (json loaded) @@ -505,7 +512,9 @@ def load(self, materials: list[Material]) -> list[Mesh]: return meshes - def load_indices(self, primitive: Primitives) -> tuple[ComponentType, npt.NDArray[Any]] | tuple[None, None]: + def load_indices( + self, primitive: Primitives + ) -> tuple[ComponentType, npt.NDArray[Any]] | tuple[None, None]: """Loads the index buffer / polygon list for a primitive""" if primitive.indices is None or primitive.accessor is None: return None, None @@ -541,7 +550,7 @@ class VBOInfo: def __init__( self, buffer: Optional[GLTFBuffer] = None, - buffer_view: Optional[GLTFBuffer]=None, + buffer_view: Optional[GLTFBuffer] = None, byte_length: int = 0, byte_offset: int = 0, component_type: ComponentType = ComponentType("", 0, 0), @@ -658,7 +667,9 @@ def __init__(self, view_id: int, data: dict[str, Any]): self.byteStride = data.get("byteStride", 0) # Valid: 34962 (ARRAY_BUFFER) and 34963 (ELEMENT_ARRAY_BUFFER) or None - def read(self, byte_offset: int = 0, dtype: Optional[type[object]] = None, count: int = 0) -> npt.NDArray[Any]: + def read( + self, byte_offset: int = 0, dtype: Optional[type[object]] = None, count: int = 0 + ) -> npt.NDArray[Any]: data = self.buffer.read( byte_offset=byte_offset + self.byteOffset, byte_length=self.byteLength, @@ -713,7 +724,7 @@ def open(self) -> None: with open(str(self.path / (self.uri if self.uri is not None else "")), "rb") as fd: self.data = fd.read() - def read(self, byte_offset: int = 0, byte_length: int = 0) -> bytes: + def read(self, byte_offset: int = 0, byte_length: int = 0) -> bytes: self.open() return self.data[byte_offset : byte_offset + byte_length] diff --git a/moderngl_window/loaders/scene/stl.py b/moderngl_window/loaders/scene/stl.py index 8734a8d..215378a 100644 --- a/moderngl_window/loaders/scene/stl.py +++ b/moderngl_window/loaders/scene/stl.py @@ -1,6 +1,5 @@ import gzip from pathlib import Path -from typing import Union import moderngl import numpy diff --git a/moderngl_window/loaders/scene/wavefront.py b/moderngl_window/loaders/scene/wavefront.py index f7f41c2..80dd24b 100644 --- a/moderngl_window/loaders/scene/wavefront.py +++ b/moderngl_window/loaders/scene/wavefront.py @@ -21,7 +21,9 @@ logger = logging.getLogger(__name__) -def translate_buffer_format(vertex_format: str, attr_names: AttributeNames) -> tuple[str, list[str], list[tuple[str, str, int]]]: +def translate_buffer_format( + vertex_format: str, attr_names: AttributeNames +) -> tuple[str, list[str], list[tuple[str, str, int]]]: """Translate the buffer format""" buffer_format = [] attributes = [] @@ -54,7 +56,9 @@ class VAOCacheLoader(cache.CacheLoader): attr_names: AttributeNames - def load_vertex_buffer(self, fd: io.TextIOWrapper, material: pywavefront.material.Material, length: int) -> None: + def load_vertex_buffer( + self, fd: io.TextIOWrapper, material: pywavefront.material.Material, length: int + ) -> None: buffer_format, attributes, mesh_attributes = translate_buffer_format( material.vertex_format, self.attr_names ) @@ -103,7 +107,9 @@ def load(self) -> Scene: VAOCacheLoader.attr_names = self.meta.attr_names data = pywavefront.Wavefront(str(path), create_materials=True, cache=self.meta.cache) - scene = Scene(self.meta.resolved_path.as_posix() if self.meta.resolved_path is not None else "") + scene = Scene( + self.meta.resolved_path.as_posix() if self.meta.resolved_path is not None else "" + ) texture_cache: dict[str, pywavefront.material.Material] = {} for _, mat in data.materials.items(): diff --git a/moderngl_window/loaders/texture/pillow.py b/moderngl_window/loaders/texture/pillow.py index 8a78319..5bac237 100644 --- a/moderngl_window/loaders/texture/pillow.py +++ b/moderngl_window/loaders/texture/pillow.py @@ -1,6 +1,6 @@ import logging from pathlib import Path -from typing import Any, Optional, Union +from typing import Optional, Union try: from PIL import Image @@ -41,7 +41,11 @@ def _open_image(self) -> Image.Image: self.image = Image.open(self.meta.resolved_path) # If the image is animated (like a gif anim) we convert it into a vertical strip - if hasattr(self.image, "is_animated") and self.image.is_animated and hasattr(self.image, "n_frames"): + if ( + hasattr(self.image, "is_animated") + and self.image.is_animated + and hasattr(self.image, "n_frames") + ): self.layers = self.image.n_frames anim = Image.new( self.image.palette.mode if self.image.palette is not None else "L", diff --git a/moderngl_window/meta/data.py b/moderngl_window/meta/data.py index d36e75b..8c79063 100644 --- a/moderngl_window/meta/data.py +++ b/moderngl_window/meta/data.py @@ -31,7 +31,9 @@ class DataDescription(ResourceDescription): default_kind: str = "" resource_type = "data" - def __init__(self, path: Optional[str] = None, kind: Optional[str] = None, **kwargs: Any) -> None: + def __init__( + self, path: Optional[str] = None, kind: Optional[str] = None, **kwargs: Any + ) -> None: """Initialize the resource description. Keyword Args: diff --git a/moderngl_window/meta/scene.py b/moderngl_window/meta/scene.py index 512fc1c..4d18b7b 100644 --- a/moderngl_window/meta/scene.py +++ b/moderngl_window/meta/scene.py @@ -33,7 +33,14 @@ class SceneDescription(ResourceDescription): default_kind = "" resource_type = "scenes" - def __init__(self, path: Optional[str] = None, kind: Optional[str] = None, cache: bool = False, attr_names: type[AttributeNames] = AttributeNames, **kwargs: Any): + def __init__( + self, + path: Optional[str] = None, + kind: Optional[str] = None, + cache: bool = False, + attr_names: type[AttributeNames] = AttributeNames, + **kwargs: Any, + ): """Create a scene description. Keyword Args: diff --git a/moderngl_window/opengl/program.py b/moderngl_window/opengl/program.py index 96122b2..3b2540a 100644 --- a/moderngl_window/opengl/program.py +++ b/moderngl_window/opengl/program.py @@ -36,7 +36,9 @@ def ctx(self) -> moderngl.Context: return moderngl_window.ctx() @classmethod - def from_single(cls: type["ProgramShaders"], meta: ProgramDescription, source: str) -> "ProgramShaders": + def from_single( + cls: type["ProgramShaders"], meta: ProgramDescription, source: str + ) -> "ProgramShaders": """Initialize a single glsl string containing all shaders""" instance = cls(meta) instance.vertex_source = ShaderSource( @@ -134,7 +136,9 @@ def from_separate( return instance @classmethod - def compute_shader(cls: type["ProgramShaders"], meta: ProgramDescription, compute_shader_source: str = "") -> "ProgramShaders": + def compute_shader( + cls: type["ProgramShaders"], meta: ProgramDescription, compute_shader_source: str = "" + ) -> "ProgramShaders": instance = cls(meta) instance.compute_shader_source = ShaderSource( COMPUTE_SHADER, @@ -157,7 +161,7 @@ def create(self) -> moderngl.Program: """ # Get out varyings out_attribs = [] - + assert self.vertex_source is not None, "There is no vertex_source to use" # If no fragment shader is present we are doing transform feedback @@ -299,7 +303,9 @@ def defines(self) -> dict[str, str]: """dict: Defines configured for this shader""" return self._defines - def handle_includes(self, load_source_func: Callable[[Any], Any], depth: int = 0, source_id: int = 0) -> None: + def handle_includes( + self, load_source_func: Callable[[Any], Any], depth: int = 0, source_id: int = 0 + ) -> None: """Inject includes into the shader source. This happens recursively up to a max level in case the users has circular includes. We also build up a list of all the included @@ -321,7 +327,9 @@ def handle_includes(self, load_source_func: Callable[[Any], Any], depth: int = 0 if line.startswith("#include"): match = re.search(r'#include\s+"?([^"]+)', line) if match is None: - raise ShaderError(f"Could not match '#include\\s+\"?([^\"]+)' in line {line}") + raise ShaderError( + f"Could not match '#include\\s+\"?([^\"]+)' in line {line}" + ) path = match[1] current_id += 1 _, source = load_source_func(path) diff --git a/moderngl_window/opengl/projection.py b/moderngl_window/opengl/projection.py index b651654..c24a3f5 100644 --- a/moderngl_window/opengl/projection.py +++ b/moderngl_window/opengl/projection.py @@ -1,13 +1,14 @@ from typing import Optional import glm -import numpy as np class Projection3D: """3D Projection""" - def __init__(self, aspect_ratio: float = 16 / 9, fov: float = 75.0, near: float = 1.0, far: float = 100.0): + def __init__( + self, aspect_ratio: float = 16 / 9, fov: float = 75.0, near: float = 1.0, far: float = 100.0 + ): """Create a 3D projection Keyword Args: diff --git a/moderngl_window/opengl/vao.py b/moderngl_window/opengl/vao.py index 2a8d615..74ccfae 100644 --- a/moderngl_window/opengl/vao.py +++ b/moderngl_window/opengl/vao.py @@ -156,7 +156,14 @@ def ctx(self) -> moderngl.Context: """moderngl.Context: The actite moderngl context""" return mglw.ctx() - def render(self, program: moderngl.Program, mode: Optional[int] = None, vertices: int = -1, first: int = 0, instances: int = 1) -> None: + def render( + self, + program: moderngl.Program, + mode: Optional[int] = None, + vertices: int = -1, + first: int = 0, + instances: int = 1, + ) -> None: """Render the VAO. An internal ``moderngl.VertexBuffer`` with compatible buffer bindings @@ -177,7 +184,15 @@ def render(self, program: moderngl.Program, mode: Optional[int] = None, vertices vao.render(mode, vertices=vertices, first=first, instances=instances) - def render_indirect(self, program: moderngl.Program, buffer: moderngl.Buffer, mode: Optional[int] = None, count: int = -1, *, first: int = 0) -> None: + def render_indirect( + self, + program: moderngl.Program, + buffer: moderngl.Buffer, + mode: Optional[int] = None, + count: int = -1, + *, + first: int = 0, + ) -> None: """ The render primitive (mode) must be the same as the input primitive of the GeometryShader. @@ -227,7 +242,12 @@ def transform( vao.transform(buffer, mode=mode, vertices=vertices, first=first, instances=instances) - def buffer(self, buffer: Union[moderngl.Buffer, npt.NDArray[Any], bytes], buffer_format: str, attribute_names: Union[list[str], str]) -> moderngl.Buffer: + def buffer( + self, + buffer: Union[moderngl.Buffer, npt.NDArray[Any], bytes], + buffer_format: str, + attribute_names: Union[list[str], str], + ) -> moderngl.Buffer: """Register a buffer/vbo for the VAO. This can be called multiple times. adding multiple buffers (interleaved or not). @@ -272,7 +292,9 @@ def buffer(self, buffer: Union[moderngl.Buffer, npt.NDArray[Any], bytes], buffer return buffer - def index_buffer(self, buffer: Union[moderngl.Buffer, npt.NDArray[Any], bytes], index_element_size: int = 4) -> None: + def index_buffer( + self, buffer: Union[moderngl.Buffer, npt.NDArray[Any], bytes], index_element_size: int = 4 + ) -> None: """Set the index buffer for this VAO. Args: diff --git a/moderngl_window/resources/programs.py b/moderngl_window/resources/programs.py index 68827bb..da7d7bc 100644 --- a/moderngl_window/resources/programs.py +++ b/moderngl_window/resources/programs.py @@ -36,10 +36,7 @@ def load(self, meta: ResourceDescription) -> moderngl.Program: Returns: moderngl.Program: The shader program """ - prog = super().load(meta) - # The tests fails with this line - # assert isinstance(prog, moderngl.Program), f"{meta} (type is {type(prog)}) do not load a moderngl.Program object, please correct this" - return prog + return super().load(meta) programs = Programs() diff --git a/moderngl_window/resources/scenes.py b/moderngl_window/resources/scenes.py index 0bc979b..f5fd6fd 100644 --- a/moderngl_window/resources/scenes.py +++ b/moderngl_window/resources/scenes.py @@ -23,7 +23,9 @@ def load(self, meta: ResourceDescription) -> Scene: :py:class:`~moderngl_window.scene.Scene`: The loaded scene """ scene = super().load(meta) - assert isinstance(scene, Scene), f"{meta} did not load a moderngl_window.scene.Scene object, please correct it." + assert isinstance( + scene, Scene + ), f"{meta} did not load a moderngl_window.scene.Scene object, please correct it." return scene diff --git a/moderngl_window/resources/textures.py b/moderngl_window/resources/textures.py index d23d770..fb0faa0 100644 --- a/moderngl_window/resources/textures.py +++ b/moderngl_window/resources/textures.py @@ -16,6 +16,7 @@ moderngl.Texture3D, ] + class Textures(BaseRegistry): """Handles texture resources""" @@ -34,7 +35,12 @@ def load(self, meta: ResourceDescription) -> TextureAny: moderngl.TextureArray: texture array if ``layers`` is supplied """ texture = super().load(meta) - assert isinstance(texture, moderngl.Texture) or isinstance(texture, moderngl.TextureArray) or isinstance(texture, moderngl.TextureCube) or isinstance(texture, moderngl.Texture3D), f"{meta} did not load a texture. Please correct it" + assert ( + isinstance(texture, moderngl.Texture) + or isinstance(texture, moderngl.TextureArray) + or isinstance(texture, moderngl.TextureCube) + or isinstance(texture, moderngl.Texture3D) + ), f"{meta} did not load a texture. Please correct it" return texture diff --git a/moderngl_window/scene/camera.py b/moderngl_window/scene/camera.py index 7a3a739..02ecbda 100644 --- a/moderngl_window/scene/camera.py +++ b/moderngl_window/scene/camera.py @@ -37,7 +37,9 @@ class Camera: print(camera.projection.matrix) """ - def __init__(self, fov: float = 60.0, aspect_ratio: float = 1.0, near: float = 1.0, far: float = 100.0): + def __init__( + self, fov: float = 60.0, aspect_ratio: float = 1.0, near: float = 1.0, far: float = 100.0 + ): """Initialize camera using a specific projection Keyword Args: @@ -124,7 +126,9 @@ def _update_yaw_and_pitch(self) -> None: self.right = glm.normalize(glm.cross(self.dir, self._up)) self.up = glm.normalize(glm.cross(self.right, self.dir)) - def look_at(self, vec: Optional[glm.vec3] = None, pos: Optional[tuple[float, float, float]] = None) -> glm.mat4: + def look_at( + self, vec: Optional[glm.vec3] = None, pos: Optional[tuple[float, float, float]] = None + ) -> glm.mat4: """Look at a specific point Either ``vec`` or ``pos`` needs to be supplied. @@ -482,7 +486,13 @@ class OrbitCamera(Camera): camera.projection.tobytes() """ - def __init__(self, target: Union[glm.vec3, tuple[float, float, float]] = (0.0, 0.0, 0.0), radius: float = 2.0, angles: tuple[float, float] = (45.0, -45.0), **kwargs: Any): + def __init__( + self, + target: Union[glm.vec3, tuple[float, float, float]] = (0.0, 0.0, 0.0), + radius: float = 2.0, + angles: tuple[float, float] = (45.0, -45.0), + **kwargs: Any, + ): """Initialize the camera Keyword Args: diff --git a/moderngl_window/scene/material.py b/moderngl_window/scene/material.py index 30a6f30..2b193ae 100644 --- a/moderngl_window/scene/material.py +++ b/moderngl_window/scene/material.py @@ -8,7 +8,9 @@ class MaterialTexture: Contains a texture and a sampler object. """ - def __init__(self, texture: Optional[moderngl.Texture] = None, sampler: Optional[moderngl.Sampler] = None): + def __init__( + self, texture: Optional[moderngl.Texture] = None, sampler: Optional[moderngl.Sampler] = None + ): """Initialize instance. Args: diff --git a/moderngl_window/scene/mesh.py b/moderngl_window/scene/mesh.py index 43382ea..9915e53 100644 --- a/moderngl_window/scene/mesh.py +++ b/moderngl_window/scene/mesh.py @@ -1,8 +1,7 @@ -from typing import TYPE_CHECKING, Any, Optional, Union +from typing import TYPE_CHECKING, Any, Optional import glm import moderngl -import numpy from moderngl_window.opengl.vao import VAO @@ -11,6 +10,7 @@ if TYPE_CHECKING: from .programs import MeshProgram + class Mesh: """Mesh info and geometry""" @@ -49,7 +49,13 @@ def __init__( self.bbox_max = bbox_max self.mesh_program: Optional["MeshProgram"] = None - def draw(self, projection_matrix: Optional[glm.mat4] = None, model_matrix: Optional[glm.mat4] = None, camera_matrix: Optional[glm.mat4] = None, time: float = 0.0) -> None: + def draw( + self, + projection_matrix: Optional[glm.mat4] = None, + model_matrix: Optional[glm.mat4] = None, + camera_matrix: Optional[glm.mat4] = None, + time: float = 0.0, + ) -> None: """Draw the mesh using the assigned mesh program Keyword Args: @@ -58,7 +64,9 @@ def draw(self, projection_matrix: Optional[glm.mat4] = None, model_matrix: Optio camera_matrix (bytes): camera_matrix """ if self.mesh_program is not None: - assert projection_matrix is not None, "Can not draw, there is no projection matrix to use" + assert ( + projection_matrix is not None + ), "Can not draw, there is no projection matrix to use" assert model_matrix is not None, "Can not draw, there is no model matrix to use" assert camera_matrix is not None, "Can not draw, there is no camera matrix to use" self.mesh_program.draw( @@ -69,7 +77,14 @@ def draw(self, projection_matrix: Optional[glm.mat4] = None, model_matrix: Optio time=time, ) - def draw_bbox(self, proj_matrix: glm.mat4, model_matrix: glm.mat4, cam_matrix: glm.mat4, program: moderngl.Program, vao: VAO) -> None: + def draw_bbox( + self, + proj_matrix: glm.mat4, + model_matrix: glm.mat4, + cam_matrix: glm.mat4, + program: moderngl.Program, + vao: VAO, + ) -> None: """Renders the bounding box for this mesh. Args: @@ -86,7 +101,9 @@ def draw_bbox(self, proj_matrix: glm.mat4, model_matrix: glm.mat4, cam_matrix: g program["bb_max"].write(self.bbox_max.to_bytes()) vao.render(program) - def draw_wireframe(self, proj_matrix: glm.mat4, model_matrix: glm.mat4, program: moderngl.Program) -> None: + def draw_wireframe( + self, proj_matrix: glm.mat4, model_matrix: glm.mat4, program: moderngl.Program + ) -> None: """Render the mesh as wireframe. proj_matrix: Projection matrix @@ -107,7 +124,9 @@ def add_attribute(self, attr_type: str, name: str, components: int) -> None: """ self.attributes[attr_type] = {"name": name, "components": components} - def calc_global_bbox(self, view_matrix: glm.mat4, bbox_min: Optional[glm.vec3], bbox_max: Optional[glm.vec3]) -> tuple[glm.vec3, glm.vec3]: + def calc_global_bbox( + self, view_matrix: glm.mat4, bbox_min: Optional[glm.vec3], bbox_max: Optional[glm.vec3] + ) -> tuple[glm.vec3, glm.vec3]: """Calculates the global bounding. Args: @@ -125,17 +144,13 @@ def calc_global_bbox(self, view_matrix: glm.mat4, bbox_min: Optional[glm.vec3], bmin = view_matrix * bb1 bmax = view_matrix * bb2 - # If a rotation happened there is an axis change and we have to ensure max-min is positive for i in range(3): if bmax[i] - bmin[i] < 0: bmin[i], bmax[i] = bmax[i], bmin[i] if bbox_min is None or bbox_max is None: - return ( - glm.vec3(bmin.x, bmin.y, bmin.z), - glm.vec3(bmax.x, bmax.y, bmax.z) - ) + return (glm.vec3(bmin.x, bmin.y, bmin.z), glm.vec3(bmax.x, bmax.y, bmax.z)) for i in range(3): bbox_min[i] = min(bbox_min[i], bmin[i]) diff --git a/moderngl_window/scene/node.py b/moderngl_window/scene/node.py index 1268615..6073e58 100644 --- a/moderngl_window/scene/node.py +++ b/moderngl_window/scene/node.py @@ -2,7 +2,7 @@ Wrapper for a loaded mesh / vao with properties """ -from typing import TYPE_CHECKING, Optional +from typing import Optional import glm import moderngl @@ -102,7 +102,12 @@ def add_child(self, node: "Node") -> None: """ self._children.append(node) - def draw(self, projection_matrix: Optional[glm.mat4], camera_matrix: Optional[glm.mat4], time: float = 0.0) -> None: + def draw( + self, + projection_matrix: Optional[glm.mat4], + camera_matrix: Optional[glm.mat4], + time: float = 0.0, + ) -> None: """Draw node and children. Keyword Args: @@ -141,7 +146,9 @@ def draw_bbox( vao: The vertex array representing the bounding box """ if self._mesh: - assert projection_matrix is not None, "Can not draw bbox, the projection matrix is empty" + assert ( + projection_matrix is not None + ), "Can not draw bbox, the projection matrix is empty" assert self._matrix_global is not None, "Can not draw bbox, the global matrix is empty" assert camera_matrix is not None, "Can not draw bbox, the camera matrix is empty" self._mesh.draw_bbox( @@ -151,7 +158,12 @@ def draw_bbox( for child in self.children: child.draw_bbox(projection_matrix, camera_matrix, program, vao) - def draw_wireframe(self, projection_matrix: Optional[glm.mat4], camera_matrix: Optional[glm.mat4], program: moderngl.Program) -> None: + def draw_wireframe( + self, + projection_matrix: Optional[glm.mat4], + camera_matrix: Optional[glm.mat4], + program: moderngl.Program, + ) -> None: """Render the node as wireframe. Keyword Args: @@ -160,14 +172,18 @@ def draw_wireframe(self, projection_matrix: Optional[glm.mat4], camera_matrix: O program (moderngl.Program): The program to render wireframe """ if self._mesh: - assert projection_matrix is not None, "Can not draw bbox, the projection matrix is empty" + assert ( + projection_matrix is not None + ), "Can not draw bbox, the projection matrix is empty" assert self._matrix_global is not None, "Can not draw bbox, the global matrix is empty" self._mesh.draw_wireframe(projection_matrix, self._matrix_global, program) for child in self.children: child.draw_wireframe(projection_matrix, self._matrix_global, program) - def calc_global_bbox(self, view_matrix: glm.mat4, bbox_min: Optional[glm.vec3], bbox_max: Optional[glm.vec3]) -> tuple[glm.vec3, glm.vec3]: + def calc_global_bbox( + self, view_matrix: glm.mat4, bbox_min: Optional[glm.vec3], bbox_max: Optional[glm.vec3] + ) -> tuple[glm.vec3, glm.vec3]: """Recursive calculation of scene bbox. Keyword Args: @@ -184,7 +200,9 @@ def calc_global_bbox(self, view_matrix: glm.mat4, bbox_min: Optional[glm.vec3], for child in self._children: bbox_min, bbox_max = child.calc_global_bbox(view_matrix, bbox_min, bbox_max) - assert (bbox_max is not None) and (bbox_min is not None), "The bounding are not defined, please make sure your code is correct" + assert (bbox_max is not None) and ( + bbox_min is not None + ), "The bounding are not defined, please make sure your code is correct" return bbox_min, bbox_max diff --git a/moderngl_window/scene/programs.py b/moderngl_window/scene/programs.py index 09bfe89..d8c077b 100644 --- a/moderngl_window/scene/programs.py +++ b/moderngl_window/scene/programs.py @@ -168,8 +168,12 @@ def draw( assert self.program is not None, "There is no program to draw" assert mesh.vao is not None, "There is no vao to render" assert mesh.material is not None, "There is no material to render" - assert mesh.material.mat_texture is not None, "The material does not have a texture to render" - assert mesh.material.mat_texture.texture is not None, "The material texture is not linked to a texture, so it can not be rendered" + assert ( + mesh.material.mat_texture is not None + ), "The material does not have a texture to render" + assert ( + mesh.material.mat_texture.texture is not None + ), "The material texture is not linked to a texture, so it can not be rendered" mesh.material.mat_texture.texture.use() self.program["m_proj"].write(projection_matrix) @@ -216,8 +220,12 @@ def draw( assert self.program is not None, "There is no program to draw" assert mesh.vao is not None, "There is no vao to render" assert mesh.material is not None, "There is no material to render" - assert mesh.material.mat_texture is not None, "The material does not have a texture to render" - assert mesh.material.mat_texture.texture is not None, "The material texture is not linked to a texture, so it can not be rendered" + assert ( + mesh.material.mat_texture is not None + ), "The material does not have a texture to render" + assert ( + mesh.material.mat_texture.texture is not None + ), "The material texture is not linked to a texture, so it can not be rendered" mesh.material.mat_texture.texture.use() self.program["m_proj"].write(projection_matrix) @@ -264,8 +272,12 @@ def draw( assert self.program is not None, "There is no program to draw" assert mesh.vao is not None, "There is no vao to render" assert mesh.material is not None, "There is no material to render" - assert mesh.material.mat_texture is not None, "The material does not have a texture to render" - assert mesh.material.mat_texture.texture is not None, "The material texture is not linked to a texture, so it can not be rendered" + assert ( + mesh.material.mat_texture is not None + ), "The material does not have a texture to render" + assert ( + mesh.material.mat_texture.texture is not None + ), "The material texture is not linked to a texture, so it can not be rendered" # if mesh.material.double_sided: # self.ctx.disable(moderngl.CULL_FACE) diff --git a/moderngl_window/scene/scene.py b/moderngl_window/scene/scene.py index 7897e69..37126be 100644 --- a/moderngl_window/scene/scene.py +++ b/moderngl_window/scene/scene.py @@ -7,7 +7,6 @@ import glm import moderngl -import numpy import moderngl_window as mglw from moderngl_window import geometry @@ -16,9 +15,15 @@ from .material import Material from .node import Node -from .programs import (ColorLightProgram, FallbackProgram, MeshProgram, - TextureLightProgram, TextureProgram, - TextureVertexColorProgram, VertexColorProgram) +from .programs import ( + ColorLightProgram, + FallbackProgram, + MeshProgram, + TextureLightProgram, + TextureProgram, + TextureVertexColorProgram, + VertexColorProgram, +) logger = logging.getLogger(__name__) @@ -147,7 +152,10 @@ def draw_bbox( node.draw_bbox(projection_matrix, camera_matrix, self.bbox_program, self.bbox_vao) def draw_wireframe( - self, projection_matrix: Optional[glm.mat4] = None, camera_matrix: Optional[glm.mat4] = None, color: tuple[float, float, float, float] = (0.75, 0.75, 0.75, 1.0) + self, + projection_matrix: Optional[glm.mat4] = None, + camera_matrix: Optional[glm.mat4] = None, + color: tuple[float, float, float, float] = (0.75, 0.75, 0.75, 1.0), ) -> None: """Render the scene in wireframe mode. @@ -173,7 +181,9 @@ def draw_wireframe( self.ctx.wireframe = False - def apply_mesh_programs(self, mesh_programs: Optional[list[MeshProgram]] = None, clear: bool = True) -> None: + def apply_mesh_programs( + self, mesh_programs: Optional[list[MeshProgram]] = None, clear: bool = True + ) -> None: """Applies mesh programs to meshes. If not mesh programs are passed in we assign default ones. @@ -224,7 +234,9 @@ def calc_scene_bbox(self) -> None: for node in self.root_nodes: bbox_min, bbox_max = node.calc_global_bbox(glm.mat4(), bbox_min, bbox_max) - assert (bbox_max is not None) and (bbox_min is not None), "The bounding are not defined, please make sure your code is correct" + assert (bbox_max is not None) and ( + bbox_min is not None + ), "The bounding are not defined, please make sure your code is correct" self.bbox_min = bbox_min self.bbox_max = bbox_max diff --git a/moderngl_window/text/bitmapped/base.py b/moderngl_window/text/bitmapped/base.py index d06ea52..925461d 100644 --- a/moderngl_window/text/bitmapped/base.py +++ b/moderngl_window/text/bitmapped/base.py @@ -44,7 +44,9 @@ def draw(self, *args: Any, **kwargs: Any) -> None: def _translate_string(self, data: str) -> Generator[int, None, None]: """Translate string into character texture positions""" - assert (self._meta is not None) and (self._ct is not None), "_meta or _ct (or both) are empty. Did you call _init()?" + assert (self._meta is not None) and ( + self._ct is not None + ), "_meta or _ct (or both) are empty. Did you call _init()?" data_bytes = data.encode("iso-8859-1", errors="replace") for index, char in enumerate(data_bytes): diff --git a/moderngl_window/utils/module_loading.py b/moderngl_window/utils/module_loading.py index ad0e323..8b0ebca 100644 --- a/moderngl_window/utils/module_loading.py +++ b/moderngl_window/utils/module_loading.py @@ -1,5 +1,5 @@ from importlib import import_module -from typing import Any, Callable +from typing import Any def import_string(dotted_path: str) -> Any: diff --git a/moderngl_window/utils/scheduler.py b/moderngl_window/utils/scheduler.py index cc0e466..ced4faa 100644 --- a/moderngl_window/utils/scheduler.py +++ b/moderngl_window/utils/scheduler.py @@ -26,7 +26,13 @@ def __init__(self, timer: BaseTimer): self._scheduler = sched.scheduler(lambda: timer.time, time.sleep) def run_once( - self, action: Callable[[Any], Any], delay: float, *, priority: int = 1, arguments: tuple[Any, ...]=(), kwargs: dict[Any, Any] = dict() + self, + action: Callable[[Any], Any], + delay: float, + *, + priority: int = 1, + arguments: tuple[Any, ...] = (), + kwargs: dict[Any, Any] = dict(), ) -> int: """Schedule a function for execution after a delay. @@ -50,7 +56,15 @@ def run_once( self._event_id += 1 return self._event_id - 1 - def run_at(self, action: Callable[[Any], Any], time: float, *, priority: int = 1, arguments: tuple[Any, ...] = (), kwargs: dict[Any, Any] = dict()) -> int: + def run_at( + self, + action: Callable[[Any], Any], + time: float, + *, + priority: int = 1, + arguments: tuple[Any, ...] = (), + kwargs: dict[Any, Any] = dict(), + ) -> int: """Schedule a function to be executed at a certain time. Args: @@ -110,7 +124,14 @@ def run_every( self._event_id += 1 return self._event_id - 1 - def _recurring_event_factory(self, function: Callable[[Any], Any], arguments: tuple[Any, ...], kwargs: dict[Any, Any], scheduling_info: tuple[Any, Any], id: int) -> Callable[[], None]: + def _recurring_event_factory( + self, + function: Callable[[Any], Any], + arguments: tuple[Any, ...], + kwargs: dict[Any, Any], + scheduling_info: tuple[Any, Any], + id: int, + ) -> Callable[[], None]: """Factory for creating recurring events that will reschedule themselves. Args: