diff --git a/pooltool/__init__.py b/pooltool/__init__.py old mode 100755 new mode 100644 index c55b02d1..37e2c378 --- a/pooltool/__init__.py +++ b/pooltool/__init__.py @@ -54,44 +54,39 @@ from pooltool.system import MultiSystem, System __all__ = [ - # subpackages - "events", - "evolution", - "game", - "objects", - "physics", - "ptmath", - "ruleset", - "system", - "utils", - # submodules - "constants", - "interact", - "layouts", - # non-documented - "serialize", - "image", - "ai", - "pot", - "aim", - # objects - "EventType", - "GameType", - "Game", "Ball", "BallParams", "Cue", - "Table", - "TableType", - "Player", + "EventType", + "Game", + "GameType", "MultiSystem", + "Player", "System", - # functions + "Table", + "TableType", + "ai", + "aim", + "constants", "continuize", - "interpolate_ball_states", - "simulate", - "show", + "events", + "evolution", + "game", "generate_layout", "get_rack", "get_ruleset", + "image", + "interact", + "interpolate_ball_states", + "layouts", + "objects", + "physics", + "pot", + "ptmath", + "ruleset", + "serialize", + "show", + "simulate", + "system", + "utils", ] diff --git a/pooltool/ai/aim/core.py b/pooltool/ai/aim/core.py index eff32603..852bf398 100644 --- a/pooltool/ai/aim/core.py +++ b/pooltool/ai/aim/core.py @@ -128,7 +128,7 @@ def _at_ball(cue_ball: Ball, object_ball: Ball, cut: float = 0.0) -> float: assert -89.0 <= cut <= 89.0, "Cut must be less than 89 and more than -89" - left = True if cut < 0 else False + left = cut < 0 cut = np.abs(cut) * np.pi / 180 R = object_ball.params.R d = ptmath.norm3d(object_ball.state.rvw[0] - cue_ball.state.rvw[0]) diff --git a/pooltool/ai/pot/core.py b/pooltool/ai/pot/core.py index 1a6a388f..34ab7253 100644 --- a/pooltool/ai/pot/core.py +++ b/pooltool/ai/pot/core.py @@ -331,13 +331,13 @@ def open_pockets(ball: Ball, table: Table, balls: Iterable[Ball]) -> set[str]: See also: viable_pockets """ - return set( + return { pocket.id for pocket in table.pockets.values() if not is_pocket_occluded(ball, table, pocket, balls) and is_room_for_cue_ball(ball, table, pocket, balls) and not is_jaw_in_way(ball, table, pocket) - ) + } def required_precision( diff --git a/pooltool/ani/action.py b/pooltool/ani/action.py index a405a47b..3d6071c6 100644 --- a/pooltool/ani/action.py +++ b/pooltool/ani/action.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from pooltool.utils.strenum import StrEnum, auto diff --git a/pooltool/ani/animate.py b/pooltool/ani/animate.py old mode 100755 new mode 100644 index dd973291..0d109271 --- a/pooltool/ani/animate.py +++ b/pooltool/ani/animate.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - import gc import sys from collections.abc import Generator @@ -339,7 +337,8 @@ class ShotViewer(Interface): For usage, see :meth:`show`. """ - def __init__(self, config=ShowBaseConfig.default()): + def __init__(self, config=None): + config = ShowBaseConfig.default() if config is None else config Interface.__init__(self, config=config) self._create_title("") @@ -425,10 +424,10 @@ def show( if settings.graphics.hud: hud.init(hide=[HUDElement.help_text]) - params = dict( - build_animations=True, - playback_mode=PlaybackMode.LOOP, - ) + params = { + "build_animations": True, + "playback_mode": PlaybackMode.LOOP, + } Global.mode_mgr.update_event_baseline() Global.mode_mgr.change_mode(Mode.shot, enter_kwargs=params) Global.task_mgr.run() @@ -469,7 +468,8 @@ def _stop(self): class Game(Interface): """This class runs the pooltool application""" - def __init__(self, config=ShowBaseConfig.default()): + def __init__(self, config=None): + config = ShowBaseConfig.default() if config is None else config Interface.__init__(self, config=config) # This task chain allows simulations to be run in parallel to the game processes @@ -541,7 +541,7 @@ def start(self): __all__ = [ + "FrameStepper", "Game", "ShotViewer", - "FrameStepper", ] diff --git a/pooltool/ani/camera/__init__.py b/pooltool/ani/camera/__init__.py index a18f415b..e722a2c8 100644 --- a/pooltool/ani/camera/__init__.py +++ b/pooltool/ani/camera/__init__.py @@ -4,8 +4,8 @@ cam = Camera() __all__ = [ - "cam", "Camera", "CameraState", + "cam", "camera_states", ] diff --git a/pooltool/ani/camera/_camera.py b/pooltool/ani/camera/_camera.py index 4ab443c9..e2f71b52 100644 --- a/pooltool/ani/camera/_camera.py +++ b/pooltool/ani/camera/_camera.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from pathlib import Path @@ -19,6 +17,7 @@ ) from pooltool.ani.globals import Global, require_showbase from pooltool.ani.mouse import mouse +from pooltool.error import PoolToolError from pooltool.objects.table.datatypes import Table from pooltool.ptmath import wiggle from pooltool.serialize import conversion @@ -90,10 +89,8 @@ def rotate_via_mouse(self, fine_control: bool = False, theta_only: bool = False) theta = self.theta + dtheta phi = self.phi + dphi - if theta > 90: - theta = 90 - if theta < 0: - theta = 0 + theta = min(theta, 90) + theta = max(theta, 0) if theta_only: self.rotate(theta=theta) @@ -159,7 +156,7 @@ def fixate(self, pos, node): def store_state(self, name, overwrite=False): """Store the current camera state in self.states""" if name in self.states and not overwrite: - raise Exception(f"Camera :: '{name}' is already a camera state") + raise PoolToolError(f"Camera :: '{name}' is already a camera state") self.states[name] = self.state self.last_state = name @@ -170,7 +167,7 @@ def load_saved_state(self, name, ok_if_not_exists=False): if ok_if_not_exists: return else: - raise Exception(f"Camera :: '{name}' is not a camera state") + raise PoolToolError(f"Camera :: '{name}' is not a camera state") self.load_state(self.states[name]) diff --git a/pooltool/ani/collision.py b/pooltool/ani/collision.py index a4aeb470..537bdce7 100644 --- a/pooltool/ani/collision.py +++ b/pooltool/ani/collision.py @@ -77,8 +77,7 @@ def collision_task(self, task): for entry in self.collision_handler.entries: min_theta = self.process_collision(entry) - if min_theta > max_min_theta: - max_min_theta = min_theta + max_min_theta = max(max_min_theta, min_theta) self.min_theta = max_min_theta return task.cont @@ -108,11 +107,6 @@ def process_cushion_collision(self, entry): self.avoid_nodes["scene"] ) - # Center ofthe cueing ball - Bx, By, Bz = self.avoid_nodes["cue_stick_focus"].getPos( - self.avoid_nodes["scene"] - ) - # The desired point where cue contacts collision plane, excluding cue width Dx, Dy, Dz = Px, Py, cushion_height diff --git a/pooltool/ani/constants.py b/pooltool/ani/constants.py index f875f423..da73c5ab 100644 --- a/pooltool/ani/constants.py +++ b/pooltool/ani/constants.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from pathlib import Path diff --git a/pooltool/ani/environment.py b/pooltool/ani/environment.py index 75c7df83..b38f13f1 100644 --- a/pooltool/ani/environment.py +++ b/pooltool/ani/environment.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from panda3d.core import ( AmbientLight, DirectionalLight, diff --git a/pooltool/ani/hud.py b/pooltool/ani/hud.py index 8d4777c1..a19445e0 100644 --- a/pooltool/ani/hud.py +++ b/pooltool/ani/hud.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from abc import ABC, abstractmethod from collections import deque @@ -42,8 +40,9 @@ def __init__(self): self.elements = None self.initialized = False - def init(self, hide: list[HUDElement] = list()): + def init(self, hide: list[HUDElement] | None = None): """Initialize HUD elements and start the HUD update task""" + hide = [] if hide is None else hide self.elements = { HUDElement.help_text: Help(), @@ -475,10 +474,8 @@ def set(self, V0): self.text.setText(f"{V0:.2f} m/s") value = (V0 - min_stroke_speed) / (max_stroke_speed - min_stroke_speed) - if value < 0: - value = 0 - if value > 1: - value = 1 + value = max(value, 0) + value = min(value, 1) self.fg.setScale(value, 1, 1) self.bg.setScale(1.0 - value, 1, 1) diff --git a/pooltool/ani/image/__init__.py b/pooltool/ani/image/__init__.py index 6849464b..cf1fdd4f 100644 --- a/pooltool/ani/image/__init__.py +++ b/pooltool/ani/image/__init__.py @@ -14,16 +14,16 @@ from pooltool.ani.image.utils import ImageExt, gif, rgb2gray __all__ = [ - "save_images", - "image_stack", + "GzipArrayImages", + "HDF5Images", "ImageExt", + "ImageStorageMethod", "ImageZip", - "HDF5Images", - "GzipArrayImages", "NpyImages", + "get_graphics_texture", "gif", - "rgb2gray", "image_array_from_texture", - "get_graphics_texture", - "ImageStorageMethod", + "image_stack", + "rgb2gray", + "save_images", ] diff --git a/pooltool/ani/menu/__init__.py b/pooltool/ani/menu/__init__.py index 87a4611a..f5443c66 100644 --- a/pooltool/ani/menu/__init__.py +++ b/pooltool/ani/menu/__init__.py @@ -8,6 +8,6 @@ MenuRegistry.register(SettingsMenu) __all__ = [ - "MenuRegistry", "MenuNavigator", + "MenuRegistry", ] diff --git a/pooltool/ani/menu/_datatypes.py b/pooltool/ani/menu/_datatypes.py index b1d8da69..b8cf566c 100644 --- a/pooltool/ani/menu/_datatypes.py +++ b/pooltool/ani/menu/_datatypes.py @@ -522,7 +522,7 @@ def create( def _command(text: str) -> None: try: cleaned_value = command(text) - except Exception as e: + except Exception as e: # noqa: BLE001 input_field.reset_value() input_field._show_error_message(str(e)) return diff --git a/pooltool/ani/menu/_registry.py b/pooltool/ani/menu/_registry.py index bd58af40..efa43837 100644 --- a/pooltool/ani/menu/_registry.py +++ b/pooltool/ani/menu/_registry.py @@ -1,13 +1,14 @@ import sys from collections.abc import Callable +from typing import ClassVar from pooltool.ani.globals import Global from pooltool.ani.menu._datatypes import BaseMenu class MenuRegistry: - _menus: dict[str, type[BaseMenu]] = {} - _current_menu: BaseMenu | None = None + _menus: ClassVar[dict[str, type[BaseMenu]]] = {} + _current_menu: ClassVar[BaseMenu | None] = None @classmethod def register(cls, menu_class: type[BaseMenu]) -> None: diff --git a/pooltool/ani/modes/__init__.py b/pooltool/ani/modes/__init__.py index 6e43e1cf..7a54a089 100644 --- a/pooltool/ani/modes/__init__.py +++ b/pooltool/ani/modes/__init__.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from pooltool.ani.modes.aim import AimMode from pooltool.ani.modes.ball_in_hand import BallInHandMode from pooltool.ani.modes.calculate import CalculateMode @@ -19,8 +17,6 @@ __all__ = [ - "Mode", - "ModeManager", "AimMode", "BallInHandMode", "CalculateMode", @@ -29,6 +25,8 @@ "CamSaveMode", "GameOverMode", "MenuMode", + "Mode", + "ModeManager", "PickBallMode", "PurgatoryMode", "ShotMode", diff --git a/pooltool/ani/modes/aim.py b/pooltool/ani/modes/aim.py index 477f4fc1..88aa759d 100644 --- a/pooltool/ani/modes/aim.py +++ b/pooltool/ani/modes/aim.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +from typing import ClassVar import numpy as np @@ -29,7 +29,7 @@ class AimMode(BaseMode): name = Mode.aim - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.rotate_cue_left: False, Action.rotate_cue_right: False, Action.fine_control: False, @@ -128,7 +128,7 @@ def exit(self): def aim_task(self, task): if self.keymap[Action.view]: - Global.mode_mgr.change_mode(Mode.view, enter_kwargs=dict(move_active=True)) + Global.mode_mgr.change_mode(Mode.view, enter_kwargs={"move_active": True}) return task.done elif self.keymap[Action.stroke]: Global.mode_mgr.change_mode(Mode.stroke) @@ -203,10 +203,8 @@ def aim_apply_power(self): dy = mouse.get_dy() V0 = multisystem.active.cue.V0 + dy * power_sensitivity - if V0 < min_stroke_speed: - V0 = min_stroke_speed - if V0 > max_stroke_speed: - V0 = max_stroke_speed + V0 = max(V0, min_stroke_speed) + V0 = min(V0, max_stroke_speed) multisystem.active.cue.set_state(V0=V0) self._update_hud() diff --git a/pooltool/ani/modes/ball_in_hand.py b/pooltool/ani/modes/ball_in_hand.py index 6a4bdc7f..bade9659 100644 --- a/pooltool/ani/modes/ball_in_hand.py +++ b/pooltool/ani/modes/ball_in_hand.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +from typing import ClassVar import numpy as np from direct.interval.IntervalGlobal import Parallel @@ -23,7 +23,7 @@ class BallInHandMode(BaseMode): name = Mode.ball_in_hand - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.quit: False, Action.ball_in_hand: True, Action.next: False, @@ -113,9 +113,12 @@ def exit(self, success=False): if self.picking == "ball": self.remove_grab_selection_highlight() - if self.picking == "placement" and not success: - if self.grabbed_ball is not None: - self.grabbed_ball.set_render_state_as_object_state() + if ( + self.picking == "placement" + and not success + and self.grabbed_ball is not None + ): + self.grabbed_ball.set_render_state_as_object_state() # Clean up instruction message if it exists if self.instruction_message is not None: @@ -129,7 +132,7 @@ def ball_in_hand_task(self, task): if not self.keymap[Action.ball_in_hand]: Global.mode_mgr.change_mode( Global.mode_mgr.last_mode, - enter_kwargs=dict(load_prev_cam=False), + enter_kwargs={"load_prev_cam": False}, ) return task.done diff --git a/pooltool/ani/modes/calculate.py b/pooltool/ani/modes/calculate.py index cc56b8f8..7066758a 100644 --- a/pooltool/ani/modes/calculate.py +++ b/pooltool/ani/modes/calculate.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +from typing import ClassVar import pooltool.ani.tasks as tasks import pooltool.ani.utils as autils @@ -14,7 +14,7 @@ class CalculateMode(BaseMode): name = Mode.calculate - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.move: False, Action.quit: False, Action.zoom: False, @@ -59,7 +59,7 @@ def calculate_view_task(self, task): if not tasks.has("run_simulation"): # simulation calculation is finished Global.mode_mgr.change_mode( - Mode.shot, enter_kwargs=dict(build_animations=True) + Mode.shot, enter_kwargs={"build_animations": True} ) elif self.keymap[Action.zoom]: cam.zoom_via_mouse() diff --git a/pooltool/ani/modes/call_shot.py b/pooltool/ani/modes/call_shot.py old mode 100755 new mode 100644 index 281d168a..e126709c --- a/pooltool/ani/modes/call_shot.py +++ b/pooltool/ani/modes/call_shot.py @@ -1,5 +1,4 @@ -#! /usr/bin/env python - +from typing import ClassVar import numpy as np from direct.interval.IntervalGlobal import LerpFunc, Parallel @@ -26,7 +25,7 @@ class CallShotMode(BaseMode): name = Mode.call_shot - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.quit: False, Action.call_shot: True, Action.next: False, diff --git a/pooltool/ani/modes/cam_load.py b/pooltool/ani/modes/cam_load.py index bb853843..72ba655b 100644 --- a/pooltool/ani/modes/cam_load.py +++ b/pooltool/ani/modes/cam_load.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +from typing import ClassVar from direct.gui.DirectGui import DGG @@ -13,7 +13,7 @@ class CamLoadMode(BaseMode): name = Mode.cam_load - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.quit: False, Action.cam_load: True, } @@ -75,9 +75,7 @@ def update_load_selection(self, state, coords): def cam_load_task(self, task): if not self.keymap[Action.cam_load]: enter_kwargs = ( - dict(load_prev_cam=True) - if Global.mode_mgr.last_mode == Mode.aim - else dict() + {"load_prev_cam": True} if Global.mode_mgr.last_mode == Mode.aim else {} ) Global.mode_mgr.change_mode( Global.mode_mgr.last_mode, enter_kwargs=enter_kwargs diff --git a/pooltool/ani/modes/cam_save.py b/pooltool/ani/modes/cam_save.py index 0ac7db3d..962534bc 100644 --- a/pooltool/ani/modes/cam_save.py +++ b/pooltool/ani/modes/cam_save.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +from typing import ClassVar from direct.gui.DirectGui import DGG @@ -13,7 +13,7 @@ class CamSaveMode(BaseMode): name = Mode.cam_save - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.quit: False, Action.cam_save: True, } @@ -77,9 +77,7 @@ def update_save_selection(self, state, coords): def cam_save_task(self, task): if not self.keymap[Action.cam_save]: enter_kwargs = ( - dict(load_prev_cam=True) - if Global.mode_mgr.last_mode == Mode.aim - else dict() + {"load_prev_cam": True} if Global.mode_mgr.last_mode == Mode.aim else {} ) Global.mode_mgr.change_mode( Global.mode_mgr.last_mode, enter_kwargs=enter_kwargs diff --git a/pooltool/ani/modes/datatypes.py b/pooltool/ani/modes/datatypes.py index c97408b4..30fa2dc4 100644 --- a/pooltool/ani/modes/datatypes.py +++ b/pooltool/ani/modes/datatypes.py @@ -1,6 +1,7 @@ import copy -import pdb +import pdb # noqa: T100 from abc import ABC, abstractmethod +from typing import ClassVar import pooltool.ani.tasks as tasks from pooltool.ani.action import Action @@ -27,13 +28,13 @@ class Mode(StrEnum): class BaseMode(ABC): - keymap: dict[Action, bool] = {} + default_keymap: ClassVar[dict[Action, bool]] = {} name: Mode = Mode.none def __init__(self): - if not len(self.keymap): + if not len(self.default_keymap): raise NotImplementedError( - "Subclasses of BaseMode must have non-empty keymap" + "Subclasses of BaseMode must have non-empty default_keymap" ) if self.name == Mode.none: @@ -41,7 +42,7 @@ def __init__(self): "Subclasses of BaseMode must have 'name' attribute" ) - self.defaults = copy.deepcopy(self.keymap) + self.keymap: dict[Action, bool] = copy.deepcopy(self.default_keymap) def shared_task(self, task): if self.keymap.get(Action.quit): @@ -51,8 +52,8 @@ def shared_task(self, task): elif self.keymap.get(Action.introspect): self.keymap[Action.introspect] = False - shot = multisystem.active # noqa F841 - pdb.set_trace() + shot = multisystem.active # noqa: F841 + pdb.set_trace() # noqa: T100 elif self.keymap.get(Action.show_help): self.keymap[Action.show_help] = False @@ -74,7 +75,7 @@ def register_keymap_event(self, keystroke, action_name, action_state): tasks.register_event(keystroke, self.update_keymap, [action_name, action_state]) def reset_action_states(self): - self.keymap = copy.deepcopy(self.defaults) + self.keymap = copy.deepcopy(self.default_keymap) @abstractmethod def enter(self): @@ -111,9 +112,12 @@ def update_event_baseline(self): """ self.baseline_events = Global.base.messenger.get_events() - def change_mode(self, mode, exit_kwargs={}, enter_kwargs={}): + def change_mode(self, mode, exit_kwargs=None, enter_kwargs=None): assert mode in Mode + exit_kwargs = {} if exit_kwargs is None else exit_kwargs + enter_kwargs = {} if enter_kwargs is None else enter_kwargs + # Teardown operations for the old mode self.end_mode(**exit_kwargs) diff --git a/pooltool/ani/modes/game_over.py b/pooltool/ani/modes/game_over.py index 6e24f654..2f8cf27c 100644 --- a/pooltool/ani/modes/game_over.py +++ b/pooltool/ani/modes/game_over.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +from typing import ClassVar import pooltool.ani.utils as autils from pooltool.ani.action import Action @@ -14,7 +14,7 @@ class GameOverMode(BaseMode): stat_font_size = 0.04 left_most = -0.33 top_most = 0.4 - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.quit: False, } diff --git a/pooltool/ani/modes/menu.py b/pooltool/ani/modes/menu.py index 92465418..ccd056f8 100644 --- a/pooltool/ani/modes/menu.py +++ b/pooltool/ani/modes/menu.py @@ -1,6 +1,5 @@ -#! /usr/bin/env python - import sys +from typing import ClassVar import pooltool.ani.tasks as tasks from pooltool.ani.action import Action @@ -13,7 +12,7 @@ class MenuMode(BaseMode): name = Mode.menu - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.exit: False, Action.new_game: False, Action.scroll_up: False, diff --git a/pooltool/ani/modes/pick_ball.py b/pooltool/ani/modes/pick_ball.py index ed1eb294..f87c98ed 100644 --- a/pooltool/ani/modes/pick_ball.py +++ b/pooltool/ani/modes/pick_ball.py @@ -1,4 +1,5 @@ -#! /usr/bin/env python +from typing import ClassVar + """A mode to for picking which ball to cue""" import numpy as np @@ -18,7 +19,7 @@ class PickBallMode(BaseMode): name = Mode.pick_ball - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.quit: False, Action.pick_ball: True, Action.done: False, diff --git a/pooltool/ani/modes/purgatory.py b/pooltool/ani/modes/purgatory.py index 63defb1b..4b0b97b5 100644 --- a/pooltool/ani/modes/purgatory.py +++ b/pooltool/ani/modes/purgatory.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +from typing import ClassVar import pooltool.ani.tasks as tasks import pooltool.ani.utils as autils @@ -28,7 +28,7 @@ class PurgatoryMode(BaseMode): """ name = Mode.purgatory - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.regain_control: False, } diff --git a/pooltool/ani/modes/shot.py b/pooltool/ani/modes/shot.py old mode 100755 new mode 100644 index 85f5802e..3cd7432b --- a/pooltool/ani/modes/shot.py +++ b/pooltool/ani/modes/shot.py @@ -1,5 +1,4 @@ -#! /usr/bin/env python - +from typing import ClassVar import pooltool.ani.tasks as tasks from pooltool.ani.action import Action @@ -17,7 +16,7 @@ class ShotMode(BaseMode): name = Mode.shot - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.aim: False, Action.move: False, Action.toggle_pause: False, @@ -203,7 +202,7 @@ def shot_view_task(self, task): # Either the user has requested to start the next shot, or the animation has # finished - Global.mode_mgr.change_mode(Mode.aim, exit_kwargs=dict(key="advance")) + Global.mode_mgr.change_mode(Mode.aim, exit_kwargs={"key": "advance"}) elif self.keymap[Action.zoom]: cam.zoom_via_mouse() @@ -241,8 +240,8 @@ def shot_animation_task(self, task): elif self.keymap[Action.undo_shot]: Global.mode_mgr.change_mode( Global.mode_mgr.mode_stroked_from, - exit_kwargs=dict(key="reset"), - enter_kwargs=dict(load_prev_cam=True), + exit_kwargs={"key": "reset"}, + enter_kwargs={"load_prev_cam": True}, ) elif self.keymap[Action.parallel] and self.view_only: diff --git a/pooltool/ani/modes/stroke.py b/pooltool/ani/modes/stroke.py index 5e391449..8d8b5a68 100644 --- a/pooltool/ani/modes/stroke.py +++ b/pooltool/ani/modes/stroke.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +from typing import ClassVar import pooltool.ani.tasks as tasks import pooltool.ani.utils as autils @@ -18,7 +18,7 @@ class StrokeMode(BaseMode): name = Mode.stroke - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.fine_control: False, Action.stroke: True, } @@ -111,11 +111,11 @@ def stroke_cue_stick(self): if newX < 0: newX = 0 - collision = True if visual.cue.is_shot() else False + collision = bool(visual.cue.is_shot()) else: collision = False cue_stick_node.setX(newX) visual.cue.append_stroke_data() - return True if collision else False + return bool(collision) diff --git a/pooltool/ani/modes/view.py b/pooltool/ani/modes/view.py old mode 100755 new mode 100644 index 31411a0f..0370a9da --- a/pooltool/ani/modes/view.py +++ b/pooltool/ani/modes/view.py @@ -1,4 +1,4 @@ -#! /usr/bin/env python +from typing import ClassVar import numpy as np @@ -28,7 +28,7 @@ class ViewMode(BaseMode): name = Mode.view - keymap = { + default_keymap: ClassVar[dict[Action, bool]] = { Action.aim: False, Action.call_shot: False, Action.fine_control: False, @@ -136,7 +136,7 @@ def view_task(self, task): elif self.keymap[Action.power]: self.view_apply_power() elif self.keymap[Action.aim]: - Global.mode_mgr.change_mode(Mode.aim, enter_kwargs=dict(load_prev_cam=True)) + Global.mode_mgr.change_mode(Mode.aim, enter_kwargs={"load_prev_cam": True}) elif self.keymap[Action.exec_shot]: self.keymap[Action.exec_shot] = False if Global.game.shot_constraints.can_shoot(): @@ -150,7 +150,7 @@ def view_task(self, task): visual.switch_to_shot(multisystem.active_index - 1) self._update_hud() Global.mode_mgr.change_mode( - Mode.shot, enter_kwargs=dict(build_animations=False) + Mode.shot, enter_kwargs={"build_animations": False} ) return task.done else: @@ -165,10 +165,8 @@ def view_apply_power(self): dy = mouse.get_dy() V0 = multisystem.active.cue.V0 + dy * power_sensitivity - if V0 < min_stroke_speed: - V0 = min_stroke_speed - if V0 > max_stroke_speed: - V0 = max_stroke_speed + V0 = max(V0, min_stroke_speed) + V0 = min(V0, max_stroke_speed) multisystem.active.cue.set_state(V0=V0) self._update_hud() diff --git a/pooltool/ani/mouse.py b/pooltool/ani/mouse.py index 8bb86231..186225f4 100644 --- a/pooltool/ani/mouse.py +++ b/pooltool/ani/mouse.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - import numpy as np from panda3d.core import ClockObject, WindowProperties diff --git a/pooltool/ani/scene.py b/pooltool/ani/scene.py index f4be6d9f..ca7163fa 100644 --- a/pooltool/ani/scene.py +++ b/pooltool/ani/scene.py @@ -292,9 +292,8 @@ def unrender_balls(self) -> None: def unrender_cue(self) -> None: self.system.cue.remove_nodes() - def buildup( - self, components: list[SceneComponents] = list(SceneComponents) - ) -> None: + def buildup(self, components: list[SceneComponents] | None = None) -> None: + components = list(SceneComponents) if components is None else components self.playback_speed = 1 if SceneComponents.TABLE in components: @@ -306,10 +305,9 @@ def buildup( if SceneComponents.ENVIRONMENT in components: self.environment.init(self.system.table._table) - def teardown( - self, components: list[SceneComponents] = list(SceneComponents) - ) -> None: + def teardown(self, components: list[SceneComponents] | None = None) -> None: """Stop animations and remove all nodes""" + components = list(SceneComponents) if components is None else components self.reset_animation() was_in_parallel_mode = self.parallel_manager.is_active diff --git a/pooltool/ani/tasks.py b/pooltool/ani/tasks.py index 96f67772..69e63b50 100644 --- a/pooltool/ani/tasks.py +++ b/pooltool/ani/tasks.py @@ -24,7 +24,7 @@ def remove(name): @require_showbase -def register_event(sequence, func, func_args=[]): +def register_event(sequence, func, func_args=None): """Register event listener that triggers based on keystroke/mouse/message Args: @@ -51,4 +51,5 @@ def register_event(sequence, func, func_args=[]): Global.base.messenger.send("test") """ + func_args = [] if func_args is None else func_args Global.base.accept(sequence, func, func_args) diff --git a/pooltool/config/__init__.py b/pooltool/config/__init__.py index 80e4ebd8..65a981cc 100644 --- a/pooltool/config/__init__.py +++ b/pooltool/config/__init__.py @@ -215,7 +215,7 @@ class GraphicsConfig: @attrs.define(kw_only=True) class GameplayConfig: - game_type: GameType = settings_field( + game_type: GameType = settings_field( # noqa: RUF009 default=GameType.NINEBALL, metadata=SettingsMetadata( display_name="Game Type", @@ -224,7 +224,7 @@ class GameplayConfig: display_type=DisplayType.DROPDOWN, ), ) - table_name: TableName = settings_field( + table_name: TableName = settings_field( # noqa: RUF009 default=TableName.SEVEN_FOOT_SHOWOOD, metadata=SettingsMetadata( display_name="Table", @@ -284,7 +284,7 @@ def load(cls, path: Path) -> Settings: @staticmethod def _attrs() -> set[str]: - return set([attr.name for attr in attrs.fields_dict(Settings).values()]) + return {attr.name for attr in attrs.fields_dict(Settings).values()} # Inherits from Settings so the LSP recognizes Settings attributes. Yet truthfully these @@ -341,7 +341,7 @@ def read(self) -> Settings: if self.path.exists(): try: cfg = Settings.load(self.path) - except Exception: + except Exception: # noqa: BLE001 full_traceback = traceback.format_exc() dump_path = self.path.parent / f".{self.path.name}" run.info( diff --git a/pooltool/constants.py b/pooltool/constants.py index c71d475c..8be919a8 100644 --- a/pooltool/constants.py +++ b/pooltool/constants.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python """Constants Notes: diff --git a/pooltool/events/__init__.py b/pooltool/events/__init__.py index 1405b0b4..5232da4a 100644 --- a/pooltool/events/__init__.py +++ b/pooltool/events/__init__.py @@ -29,26 +29,26 @@ ) __all__ = [ - "filter_ball", - "filter_time", - "filter_type", - "filter_events", - "by_type", - "by_ball", - "by_time", - "null_event", + "Agent", + "AgentType", + "Event", + "EventType", "ball_ball_collision", - "ball_linear_cushion_collision", "ball_circular_cushion_collision", + "ball_linear_cushion_collision", "ball_pocket_collision", - "stick_ball_collision", "ball_table_collision", - "spinning_stationary_transition", - "rolling_stationary_transition", + "by_ball", + "by_time", + "by_type", + "filter_ball", + "filter_events", + "filter_time", + "filter_type", + "null_event", "rolling_spinning_transition", + "rolling_stationary_transition", "sliding_rolling_transition", - "Event", - "EventType", - "AgentType", - "Agent", + "spinning_stationary_transition", + "stick_ball_collision", ] diff --git a/pooltool/events/filter.py b/pooltool/events/filter.py index 93a8b8e5..e3ba200e 100644 --- a/pooltool/events/filter.py +++ b/pooltool/events/filter.py @@ -96,9 +96,7 @@ def func(events: list[Event]) -> list[Event]: new: list[Event] = [] for event in events: - if after and event.time > t: - new.append(event) - elif not after and event.time < t: + if after and event.time > t or not after and event.time < t: new.append(event) return new diff --git a/pooltool/evolution/__init__.py b/pooltool/evolution/__init__.py index 3a98993b..ae7f2c16 100644 --- a/pooltool/evolution/__init__.py +++ b/pooltool/evolution/__init__.py @@ -7,6 +7,6 @@ __all__ = [ "SimulationEngine", "continuize", - "simulate", "interpolate_ball_states", + "simulate", ] diff --git a/pooltool/evolution/event_based/cache.py b/pooltool/evolution/event_based/cache.py index ca2528e6..184056e7 100644 --- a/pooltool/evolution/event_based/cache.py +++ b/pooltool/evolution/event_based/cache.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations import attrs @@ -60,7 +58,8 @@ def update(self, event: Event) -> None: """Update transition cache for all balls in Event""" for agent in event.agents: if agent.agent_type == AgentType.BALL: - assert isinstance(ball := agent.final, Ball) + ball = agent.final + assert isinstance(ball, Ball) self.transitions[agent.id] = _next_transition(ball) @classmethod diff --git a/pooltool/evolution/event_based/detect/ball_ball.py b/pooltool/evolution/event_based/detect/ball_ball.py index df782764..f654f722 100644 --- a/pooltool/evolution/event_based/detect/ball_ball.py +++ b/pooltool/evolution/event_based/detect/ball_ball.py @@ -149,11 +149,13 @@ def get_next_ball_ball_event( ball2_state = ball2.state ball2_params = ball2.params - if ball1_state.s == const.pocketed or ball2_state.s == const.pocketed: - cache[ball_pair] = np.inf - elif ( - ball1_state.s in const.nontranslating - and ball2_state.s in const.nontranslating + if ( + ball1_state.s == const.pocketed + or ball2_state.s == const.pocketed + or ( + ball1_state.s in const.nontranslating + and ball2_state.s in const.nontranslating + ) ): cache[ball_pair] = np.inf elif ptmath.is_overlapping( diff --git a/pooltool/evolution/event_based/detect/ball_cushion.py b/pooltool/evolution/event_based/detect/ball_cushion.py index 713db1b9..6363358b 100644 --- a/pooltool/evolution/event_based/detect/ball_cushion.py +++ b/pooltool/evolution/event_based/detect/ball_cushion.py @@ -205,8 +205,7 @@ def ball_vertical_plane_collision_time( if not (0 <= s_score <= 1): continue - if root.real < min_time: - min_time = root.real + min_time = min(min_time, root.real) return min_time diff --git a/pooltool/evolution/event_based/introspection.py b/pooltool/evolution/event_based/introspection.py index e0607557..1b226232 100644 --- a/pooltool/evolution/event_based/introspection.py +++ b/pooltool/evolution/event_based/introspection.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python """Event-based simulation introspection tools. This module provides utilities for capturing and analyzing simulation state at each diff --git a/pooltool/evolution/event_based/simulate.py b/pooltool/evolution/event_based/simulate.py old mode 100755 new mode 100644 index f82996e8..a6bd231b --- a/pooltool/evolution/event_based/simulate.py +++ b/pooltool/evolution/event_based/simulate.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations import attrs diff --git a/pooltool/layouts.py b/pooltool/layouts.py old mode 100755 new mode 100644 index c0437579..544472b6 --- a/pooltool/layouts.py +++ b/pooltool/layouts.py @@ -444,7 +444,7 @@ def _get_snooker_rack( if ballset is None: ballset = DEFAULT_SNOOKER_BALLSET - red_ids = set([f"red_{i:02d}" for i in range(1, 16)]) + red_ids = {f"red_{i:02d}" for i in range(1, 16)} first_red_y = 0.75 + (2 * (spacing_factor + 1) * ball_params.R / table.l) @@ -548,14 +548,14 @@ def get_rack( __all__ = [ - "DEFAULT_STANDARD_BALLSET", "DEFAULT_SNOOKER_BALLSET", - "DEFAULT_THREECUSH_BALLSET", + "DEFAULT_STANDARD_BALLSET", "DEFAULT_SUMTOTHREE_BALLSET", + "DEFAULT_THREECUSH_BALLSET", + "BallPos", "Dir", "Jump", "Pos", - "BallPos", "Translation", "ball_cluster_blueprint", "generate_layout", diff --git a/pooltool/main.py b/pooltool/main.py old mode 100755 new mode 100644 index 3a449ede..b1eb8776 --- a/pooltool/main.py +++ b/pooltool/main.py @@ -1,6 +1,3 @@ -#!/usr/bin/env python - - import attrs import click diff --git a/pooltool/objects/__init__.py b/pooltool/objects/__init__.py index 8526ddae..4458f074 100644 --- a/pooltool/objects/__init__.py +++ b/pooltool/objects/__init__.py @@ -34,28 +34,28 @@ ) __all__ = [ - "BallSet", "Ball", - "BallState", - "BallParams", - "PrebuiltBallParams", "BallHistory", "BallOrientation", - "CueSpecs", - "PrebuiltCueSpecs", - "Cue", - "Pocket", - "LinearCushionSegment", + "BallParams", + "BallSet", + "BallState", + "BilliardTableSpecs", "CircularCushionSegment", - "CushionSegments", + "Cue", + "CueSpecs", "CushionDirection", + "CushionSegments", + "LinearCushionSegment", + "Pocket", + "PocketTableSpecs", + "PrebuiltBallParams", + "PrebuiltCueSpecs", + "SnookerTableSpecs", "Table", "TableModelDescr", - "TableType", "TableName", - "PocketTableSpecs", - "BilliardTableSpecs", - "SnookerTableSpecs", + "TableType", "get_ballset", "get_ballset_names", ] diff --git a/pooltool/objects/cue/datatypes.py b/pooltool/objects/cue/datatypes.py old mode 100755 new mode 100644 index e3997f5f..7697f763 --- a/pooltool/objects/cue/datatypes.py +++ b/pooltool/objects/cue/datatypes.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from attrs import define, evolve, field, fields_dict diff --git a/pooltool/objects/cue/render.py b/pooltool/objects/cue/render.py index ea6e0c28..71a6b0c3 100644 --- a/pooltool/objects/cue/render.py +++ b/pooltool/objects/cue/render.py @@ -195,17 +195,14 @@ def is_shot(self): # There is only a handful of frames return False - if not any([x > 0 for x in self.stroke_pos]): + if not any(x > 0 for x in self.stroke_pos): # No backstroke return False backstroke_time, _, strike_time = self.get_stroke_times() - if (strike_time - backstroke_time) < 0.3: - # Stroke is too short - return False - - return True + stroke_duration = strike_time - backstroke_time + return stroke_duration >= 0.3 def calc_V0_from_stroke(self): """Calculates V0 from the stroke sequence diff --git a/pooltool/objects/datatypes.py b/pooltool/objects/datatypes.py index c7781523..98173239 100644 --- a/pooltool/objects/datatypes.py +++ b/pooltool/objects/datatypes.py @@ -56,7 +56,7 @@ def remove_nodes(self): def hide_node(self, name): self.nodes[name].hide() - def hide_nodes(self, ignore=set()): + def hide_nodes(self, ignore=frozenset()): for node_name in self.nodes: if node_name in ignore: continue @@ -65,7 +65,7 @@ def hide_nodes(self, ignore=set()): def show_node(self, name): self.nodes[name].show() - def show_nodes(self, ignore=set()): + def show_nodes(self, ignore=frozenset()): for node_name in self.nodes: if node_name in ignore: continue diff --git a/pooltool/objects/table/datatypes.py b/pooltool/objects/table/datatypes.py index 04cf7104..09ceac62 100644 --- a/pooltool/objects/table/datatypes.py +++ b/pooltool/objects/table/datatypes.py @@ -106,7 +106,7 @@ def w(self) -> float: return x2 - x1 @property - def l(self) -> float: # noqa F743 + def l(self) -> float: """The length of the table. Warning: diff --git a/pooltool/objects/table/render.py b/pooltool/objects/table/render.py index 724acbf0..c6292f58 100644 --- a/pooltool/objects/table/render.py +++ b/pooltool/objects/table/render.py @@ -60,7 +60,6 @@ def init_collisions(self): cushion = self._table.cushion_segments.linear[cushion_id] x1, y1, z1 = cushion.p1 - x2, y2, z2 = cushion.p2 n1, n2, n3 = cushion.normal if cushion_id in ["9", "12"]: diff --git a/pooltool/objects/table/specs.py b/pooltool/objects/table/specs.py old mode 100755 new mode 100644 index 07c6a84a..1708ad71 --- a/pooltool/objects/table/specs.py +++ b/pooltool/objects/table/specs.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from typing import Protocol @@ -101,7 +99,7 @@ class PocketTableSpecs: """ # 7-foot table (78x39 in^2 playing surface) - l: float = field(default=1.9812) # noqa E741 + l: float = field(default=1.9812) w: float = field(default=1.9812 / 2) cushion_width: float = field(default=2 * 2.54 / 100) @@ -173,7 +171,7 @@ class SnookerTableSpecs: # https://wpbsa.com/rules/ # The playing area is within the cushion faces and shall measure # 11 ft 8½ in x 5 ft 10 in (3569 mm x 1778 mm) with a tolerance on both dimensions of +/- ½ in (13 mm). - l: float = field(default=3.5445) # noqa E741 + l: float = field(default=3.5445) w: float = field(default=1.7465) cushion_width: float = field(default=1.55 * 25.4 / 1000) diff --git a/pooltool/physics/__init__.py b/pooltool/physics/__init__.py index c462d4e2..068950be 100644 --- a/pooltool/physics/__init__.py +++ b/pooltool/physics/__init__.py @@ -50,34 +50,32 @@ ) __all__ = [ - # Resolve - "display_models", - "Resolver", "RESOLVER_PATH", + "BallBallFrictionModel", "BallBallModel", "BallCCushionModel", "BallLCushionModel", "BallPocketModel", "BallTableModel", - "StickBallModel", "BallTransitionModel", - "rel_velocity", - "surface_velocity", - "get_u_vec", - "get_slide_time", - "get_roll_time", - "get_spin_time", - "get_airborne_time", - "get_ball_energy", - "ball_ball_models", - "BallBallFrictionModel", + "Resolver", + "StickBallModel", "ball_ball_friction_models", - "ball_lcushion_models", + "ball_ball_models", "ball_ccushion_models", + "ball_lcushion_models", "ball_pocket_models", "ball_table_models", - "stick_ball_models", "ball_transition_models", - # Evolve + "display_models", "evolve_ball_motion", + "get_airborne_time", + "get_ball_energy", + "get_roll_time", + "get_slide_time", + "get_spin_time", + "get_u_vec", + "rel_velocity", + "stick_ball_models", + "surface_velocity", ] diff --git a/pooltool/physics/evolve/__init__.py b/pooltool/physics/evolve/__init__.py index ec1dbac7..2be196de 100644 --- a/pooltool/physics/evolve/__init__.py +++ b/pooltool/physics/evolve/__init__.py @@ -136,7 +136,7 @@ def _evolve_roll_state( if t == 0: return rvw - r_0, v_0, w_0 = rvw + r_0, v_0, _w_0 = rvw v_0_hat = ptmath.unit_vector(v_0) @@ -169,9 +169,8 @@ def _evolve_perpendicular_spin_component( alpha = 5 * u_sp * g / (2 * R) - if t > np.abs(wz) / alpha: - # You can't decay past 0 angular velocity - t = np.abs(wz) / alpha + # You can't decay past 0 angular velocity + t = min(t, np.abs(wz) / alpha) # Always decay towards 0, whether spin is +ve or -ve sign = 1 if wz > 0 else -1 diff --git a/pooltool/physics/resolve/__init__.py b/pooltool/physics/resolve/__init__.py index 315a52a0..ff5d95b2 100644 --- a/pooltool/physics/resolve/__init__.py +++ b/pooltool/physics/resolve/__init__.py @@ -75,6 +75,6 @@ def display_models(): __all__ = [ - "Resolver", "RESOLVER_PATH", + "Resolver", ] diff --git a/pooltool/physics/resolve/ball_ball/frictional_inelastic/__init__.py b/pooltool/physics/resolve/ball_ball/frictional_inelastic/__init__.py index df08d381..aa63d6d3 100644 --- a/pooltool/physics/resolve/ball_ball/frictional_inelastic/__init__.py +++ b/pooltool/physics/resolve/ball_ball/frictional_inelastic/__init__.py @@ -112,7 +112,7 @@ class FrictionalInelastic(CoreBallBallCollision): more complete analysis of velocity and angular velocity in their vector forms. """ - friction: BallBallFrictionStrategy = AlciatoreBallBallFriction() + friction: BallBallFrictionStrategy = attrs.field(factory=AlciatoreBallBallFriction) model: BallBallModel = attrs.field( default=BallBallModel.FRICTIONAL_INELASTIC, init=False, repr=False diff --git a/pooltool/physics/resolve/ball_ball/frictional_mathavan/__init__.py b/pooltool/physics/resolve/ball_ball/frictional_mathavan/__init__.py index cdc41452..a7c144fd 100644 --- a/pooltool/physics/resolve/ball_ball/frictional_mathavan/__init__.py +++ b/pooltool/physics/resolve/ball_ball/frictional_mathavan/__init__.py @@ -235,7 +235,7 @@ class FrictionalMathavan(CoreBallBallCollision): https://billiards.colostate.edu/physics_articles/Mathavan_Sports_2014.pdf """ - friction: BallBallFrictionStrategy = AlciatoreBallBallFriction() + friction: BallBallFrictionStrategy = attrs.field(factory=AlciatoreBallBallFriction) num_iterations: int = 1000 model: BallBallModel = attrs.field( diff --git a/pooltool/physics/resolve/ball_cushion/mathavan_2010/__init__.py b/pooltool/physics/resolve/ball_cushion/mathavan_2010/__init__.py index 4ad97e14..a9e75949 100644 --- a/pooltool/physics/resolve/ball_cushion/mathavan_2010/__init__.py +++ b/pooltool/physics/resolve/ball_cushion/mathavan_2010/__init__.py @@ -4,6 +4,6 @@ ) __all__ = [ - "Mathavan2010Linear", "Mathavan2010Circular", + "Mathavan2010Linear", ] diff --git a/pooltool/physics/resolve/stronge_compliant.py b/pooltool/physics/resolve/stronge_compliant.py index ef6c6d2f..157467e4 100644 --- a/pooltool/physics/resolve/stronge_compliant.py +++ b/pooltool/physics/resolve/stronge_compliant.py @@ -365,7 +365,7 @@ def f(t): return t_c t_slip = sp.optimize.toms748(f, t_c, t_f) - assert t_c <= t_slip and t_slip <= t_f, f"t_c={t_c} <= t_slip={t_slip} <= t_f={t_f}" + assert t_c <= t_slip <= t_f, f"t_c={t_c} <= t_slip={t_slip} <= t_f={t_f}" return t_slip @@ -404,7 +404,7 @@ def f(t): return t_f t_slip = sp.optimize.toms748(f, t_c, t_f) - assert t_stick <= t_slip and t_slip <= t_f, ( + assert t_stick <= t_slip <= t_f, ( f"t_stick={t_stick} <= t_slip={t_slip} <= t_f={t_f}" ) return t_slip @@ -418,23 +418,25 @@ def nondimensional_stick_time_for_initial_slip( e_n: float, eta_squared: float, ) -> float: - assert mu * eta_squared <= v_t_0_by_v_n_0 and v_t_0_by_v_n_0 <= mu * ( - (1 + e_n) * beta_t_by_beta_n - eta_squared / e_n + assert ( + mu * eta_squared + <= v_t_0_by_v_n_0 + <= mu * ((1 + e_n) * beta_t_by_beta_n - eta_squared / e_n) ) if v_t_0_by_v_n_0 <= mu * beta_t_by_beta_n: x = ((v_t_0_by_v_n_0 / mu) - beta_t_by_beta_n) / ( eta_squared - beta_t_by_beta_n ) - assert -1 <= x and x <= 1, f"x={x}" + assert -1 <= x <= 1, f"x={x}" result = (2 / math.pi) * math.acos(x) - assert 0 <= result and result <= 1, f"result={result}" + assert 0 <= result <= 1, f"result={result}" else: x = ((v_t_0_by_v_n_0 / mu) - beta_t_by_beta_n) / ( eta_squared / e_n - e_n * beta_t_by_beta_n ) - assert -1 <= x and x <= 1, f"x={x}" + assert -1 <= x <= 1, f"x={x}" result = (2 / math.pi) * (math.acos(x) - t_c_shift(e_n)) * e_n - assert 1 <= result and result <= (1 + e_n), f"result={result}, e_n={e_n}" + assert 1 <= result <= (1 + e_n), f"result={result}, e_n={e_n}" return result diff --git a/pooltool/ptmath/__init__.py b/pooltool/ptmath/__init__.py index 5f7ed085..56b57535 100644 --- a/pooltool/ptmath/__init__.py +++ b/pooltool/ptmath/__init__.py @@ -25,25 +25,25 @@ ) __all__ = [ - "roots", "angle", "angle_between_vectors", + "are_points_on_same_side", + "convert_2D_to_3D", "coordinate_rotation", "cross", "decompose_normal_tangent", - "squared_norm3d", + "find_intersection_2D", + "is_overlapping", + "norm2d", "norm3d", + "point_on_line_closest_to_point", + "quaternion_from_vector_to_vector", + "roots", + "rotation_from_vector_to_vector", "solve_transcendental", - "convert_2D_to_3D", "squared_norm2d", - "norm2d", - "point_on_line_closest_to_point", - "find_intersection_2D", - "are_points_on_same_side", + "squared_norm3d", "unit_vector", "unit_vector_slow", "wiggle", - "rotation_from_vector_to_vector", - "quaternion_from_vector_to_vector", - "is_overlapping", ] diff --git a/pooltool/ptmath/roots/__init__.py b/pooltool/ptmath/roots/__init__.py index f154ac27..f455eac7 100644 --- a/pooltool/ptmath/roots/__init__.py +++ b/pooltool/ptmath/roots/__init__.py @@ -7,9 +7,9 @@ ) __all__ = [ - "quadratic", - "quartic", - "is_real_number", "get_real_positive_smallest_root", "get_real_positive_smallest_roots", + "is_real_number", + "quadratic", + "quartic", ] diff --git a/pooltool/ptmath/roots/_quartic_numba.py b/pooltool/ptmath/roots/_quartic_numba.py index 7a405f44..800bdd7d 100644 --- a/pooltool/ptmath/roots/_quartic_numba.py +++ b/pooltool/ptmath/roots/_quartic_numba.py @@ -193,8 +193,7 @@ def oqs_calc_phi0(a, b, c, d, scaled): maxtt = abs(xxx) else: maxtt = abs(gx) - if abs(h) > maxtt: - maxtt = abs(h) + maxtt = max(maxtt, abs(h)) if abs(f) > macheps * maxtt: for iter in range(8): diff --git a/pooltool/ptmath/utils.py b/pooltool/ptmath/utils.py index b77b4282..58d0f3e4 100644 --- a/pooltool/ptmath/utils.py +++ b/pooltool/ptmath/utils.py @@ -179,8 +179,11 @@ def unit_vector( return vector / norm +_X_AXIS = np.array([1, 0]) + + @jit(nopython=True, cache=const.use_numba_cache) -def angle(v2: NDArray[np.float64], v1: NDArray[np.float64] = np.array([1, 0])) -> float: +def angle(v2: NDArray[np.float64], v1: NDArray[np.float64] = _X_AXIS) -> float: """Returns counter-clockwise angle of projections of v1 and v2 onto the x-y plane (just-in-time compiled) diff --git a/pooltool/ruleset/__init__.py b/pooltool/ruleset/__init__.py old mode 100755 new mode 100644 index 336968fe..091e0f92 --- a/pooltool/ruleset/__init__.py +++ b/pooltool/ruleset/__init__.py @@ -62,25 +62,25 @@ def get_ruleset(game: GameType, enforce_rules: bool = True) -> type[Ruleset]: __all__ = [ "AIPlayer", - "Player", "BallInHandOptions", + "Player", + "Ruleset", "ShotConstraints", "ShotInfo", - "Ruleset", - "get_ruleset", + "balls_that_hit_cushion", + "get_ball_ids_on_table", + "get_highest_ball", + "get_id_of_first_ball_hit", + "get_lowest_ball", "get_pocketed_ball_ids", "get_pocketed_ball_ids_during_shot", - "get_id_of_first_ball_hit", + "get_ruleset", + "is_ball_hit", "is_ball_pocketed", "is_ball_pocketed_in_pocket", - "is_target_group_hit_first", - "respot", - "get_ball_ids_on_table", - "get_lowest_ball", - "get_highest_ball", "is_lowest_hit_first", - "balls_that_hit_cushion", - "is_ball_hit", "is_numbered_ball_pocketed", "is_shot_called_if_required", + "is_target_group_hit_first", + "respot", ] diff --git a/pooltool/ruleset/datatypes.py b/pooltool/ruleset/datatypes.py index bbf21705..f680b79e 100644 --- a/pooltool/ruleset/datatypes.py +++ b/pooltool/ruleset/datatypes.py @@ -1,4 +1,3 @@ -#! /usr/bin/env python from __future__ import annotations import copy @@ -128,21 +127,17 @@ def cueball(self, balls: dict[str, Any]) -> str: if cue in balls: return cue - return list(balls.keys())[0] + return next(iter(balls)) return self.cueable[0] def can_shoot(self) -> bool: - if ( + return bool( self.call_shot and self.ball_call is not None and self.pocket_call is not None - ): - return True - elif not self.call_shot: - return True - else: - return False + or not self.call_shot + ) @attrs.define(frozen=True) @@ -324,4 +319,3 @@ def copy(self) -> Ruleset: def copy(self): return self """ - pass diff --git a/pooltool/ruleset/eight_ball.py b/pooltool/ruleset/eight_ball.py old mode 100755 new mode 100644 index 971fb1ef..7b015ed8 --- a/pooltool/ruleset/eight_ball.py +++ b/pooltool/ruleset/eight_ball.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from collections import Counter @@ -148,10 +146,9 @@ def is_turn_over(shot: System, constraints: ShotConstraints, legal: bool) -> boo assert constraints.ball_call is not None assert constraints.pocket_call is not None - if is_ball_pocketed_in_pocket(shot, constraints.ball_call, constraints.pocket_call): - return False - - return True + return not is_ball_pocketed_in_pocket( + shot, constraints.ball_call, constraints.pocket_call + ) def is_game_over(shot: System) -> bool: @@ -272,8 +269,10 @@ def get_score(self, shot: System) -> Counter: num_active = num_stripes num_other = num_solids - assert (ball_call := self.shot_constraints.ball_call) == "8" - assert (pocket_call := self.shot_constraints.pocket_call) is not None + ball_call = self.shot_constraints.ball_call + pocket_call = self.shot_constraints.pocket_call + assert ball_call == "8" + assert pocket_call is not None if ( is_ball_pocketed_in_pocket(shot, ball_call, pocket_call) and self.shot_info.legal diff --git a/pooltool/ruleset/nine_ball.py b/pooltool/ruleset/nine_ball.py index 76651ad6..80ef9c0b 100644 --- a/pooltool/ruleset/nine_ball.py +++ b/pooltool/ruleset/nine_ball.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations import copy @@ -97,10 +95,7 @@ def is_turn_over(shot: System, legal: bool) -> bool: ids = get_pocketed_ball_ids_during_shot(shot, exclude={"cue"}) - if len(ids): - return False - - return True + return not len(ids) def is_game_over(shot: System, legal: bool) -> bool: diff --git a/pooltool/ruleset/sandbox.py b/pooltool/ruleset/sandbox.py old mode 100755 new mode 100644 index e4d8a92b..b3778099 --- a/pooltool/ruleset/sandbox.py +++ b/pooltool/ruleset/sandbox.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from collections import Counter @@ -32,7 +30,7 @@ def initial_shot_constraints(self) -> ShotConstraints: ball_in_hand=BallInHandOptions.ANYWHERE, movable=None, cueable=None, - hittable=tuple(), + hittable=(), call_shot=False, ) diff --git a/pooltool/ruleset/snooker/balls.py b/pooltool/ruleset/snooker/balls.py index 9aa45b58..7b9a26b2 100644 --- a/pooltool/ruleset/snooker/balls.py +++ b/pooltool/ruleset/snooker/balls.py @@ -53,7 +53,8 @@ def __attrs_post_init__(self): if self.id == "red": return - assert isinstance((loc := snooker_color_locs[self.id].relative_to), tuple) + loc = snooker_color_locs[self.id].relative_to + assert isinstance(loc, tuple) self.respot = loc diff --git a/pooltool/ruleset/snooker/core.py b/pooltool/ruleset/snooker/core.py index 5ad88f6a..cecc50d0 100644 --- a/pooltool/ruleset/snooker/core.py +++ b/pooltool/ruleset/snooker/core.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from collections import Counter @@ -117,10 +115,7 @@ def is_game_over(shot: System, legal: bool) -> bool: # Foul on black at this stage is an end of frame return True - if "black" in get_pocketed_ball_ids_during_shot(shot, exclude={"white"}): - return True - - return False + return "black" in get_pocketed_ball_ids_during_shot(shot, exclude={"white"}) def decide_winner( @@ -191,7 +186,7 @@ def next_shot_constraints(self, shot: System) -> ShotConstraints: if self.phase is GamePhase.ALTERNATING: hittable = ball_group.balls - call_shot = True if ball_group is BallGroup.COLORS else False + call_shot = ball_group is BallGroup.COLORS ball_call = None else: lowest = get_lowest_pottable(shot) @@ -224,7 +219,8 @@ def respot_balls(self, shot: System): if self.phase is GamePhase.ALTERNATING: check.extend(list(BallGroup.COLORS.balls)) else: - assert (ball_call := self.shot_constraints.ball_call) is not None + ball_call = self.shot_constraints.ball_call + assert ball_call is not None check.extend( get_color_balls_to_be_potted( shot, diff --git a/pooltool/ruleset/snooker/utils.py b/pooltool/ruleset/snooker/utils.py index faa50f52..92a70a36 100644 --- a/pooltool/ruleset/snooker/utils.py +++ b/pooltool/ruleset/snooker/utils.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from collections.abc import Iterable diff --git a/pooltool/ruleset/sum_to_three.py b/pooltool/ruleset/sum_to_three.py index ed97ab6d..5e2d9194 100644 --- a/pooltool/ruleset/sum_to_three.py +++ b/pooltool/ruleset/sum_to_three.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from collections import Counter diff --git a/pooltool/ruleset/three_cushion.py b/pooltool/ruleset/three_cushion.py index bcd279f8..43b18345 100644 --- a/pooltool/ruleset/three_cushion.py +++ b/pooltool/ruleset/three_cushion.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from collections import Counter @@ -21,7 +19,7 @@ def _other(cue: str, event: Event) -> str: if id != cue: return id - raise Exception() + raise ValueError(f"Event {event} does not involve a ball other than '{cue}'") def is_point(shot: System) -> bool: @@ -115,12 +113,13 @@ def initial_shot_constraints(self) -> ShotConstraints: ball_in_hand=BallInHandOptions.NONE, movable=[], cueable=["white"], - hittable=tuple(), + hittable=(), call_shot=False, ) def next_shot_constraints(self, shot: System) -> ShotConstraints: - assert (cueable := self.shot_constraints.cueable) is not None + cueable = self.shot_constraints.cueable + assert cueable is not None if self.shot_info.turn_over: cueable = [next_cue(cueable[0], len(self.players))] @@ -129,7 +128,7 @@ def next_shot_constraints(self, shot: System) -> ShotConstraints: ball_in_hand=BallInHandOptions.NONE, movable=[], cueable=cueable, - hittable=tuple(), + hittable=(), call_shot=False, ) diff --git a/pooltool/ruleset/utils.py b/pooltool/ruleset/utils.py index 7381e6fe..d75d7901 100644 --- a/pooltool/ruleset/utils.py +++ b/pooltool/ruleset/utils.py @@ -99,12 +99,12 @@ def get_ball_ids_on_table( shot: System, at_start: bool, exclude: set[str] | None = None ) -> set[str]: history_idx = 0 if at_start else -1 - return set( + return { ball.id for ball in shot.balls.values() if ball.history[history_idx].s in const.on_table and (exclude is None or ball.id not in exclude) - ) + } class StateProbe(StrEnum): @@ -197,7 +197,7 @@ def balls_that_hit_cushion(shot: System, exclude: set[str] | None = None) -> set by_ball(numbered_ball_ids), ) - return set(event.agents[0].id for event in cushion_events) + return {event.agents[0].id for event in cushion_events} def is_ball_hit(shot: System) -> bool: @@ -212,7 +212,6 @@ def is_shot_called_if_required(shot_constraints: ShotConstraints) -> bool: if not shot_constraints.call_shot: return True - if shot_constraints.ball_call is None or shot_constraints.pocket_call is None: - return False - - return True + return not ( + shot_constraints.ball_call is None or shot_constraints.pocket_call is None + ) diff --git a/pooltool/serialize/__init__.py b/pooltool/serialize/__init__.py index 7e24cbb5..47700f62 100644 --- a/pooltool/serialize/__init__.py +++ b/pooltool/serialize/__init__.py @@ -55,13 +55,13 @@ __all__ = [ "Convert", - "conversion", + "Pathish", "SerializeFormat", - "to_json", - "to_msgpack", - "to_yaml", + "conversion", "from_json", "from_msgpack", "from_yaml", - "Pathish", + "to_json", + "to_msgpack", + "to_yaml", ] diff --git a/pooltool/system/__init__.py b/pooltool/system/__init__.py index 2da2924b..12034589 100644 --- a/pooltool/system/__init__.py +++ b/pooltool/system/__init__.py @@ -4,8 +4,8 @@ from pooltool.system.render import SystemRender __all__ = [ - "System", "MultiSystem", - "multisystem", + "System", "SystemRender", + "multisystem", ] diff --git a/pooltool/system/datatypes.py b/pooltool/system/datatypes.py index ce6b8374..48984c58 100644 --- a/pooltool/system/datatypes.py +++ b/pooltool/system/datatypes.py @@ -1,5 +1,3 @@ -#! /usr/bin/env python - from __future__ import annotations from collections.abc import Iterator diff --git a/pooltool/utils/__init__.py b/pooltool/utils/__init__.py index c4d4c4b0..bd5ea846 100644 --- a/pooltool/utils/__init__.py +++ b/pooltool/utils/__init__.py @@ -59,7 +59,7 @@ def __init__(self): self.timer_start = self.timestamp() def timestamp(self) -> datetime.datetime: - return datetime.datetime.fromtimestamp(time.time()) + return datetime.datetime.now(tz=datetime.timezone.utc).astimezone() def timedelta_to_checkpoint( self, timestamp: datetime.datetime @@ -165,8 +165,8 @@ def get_total_memory_usage() -> int: for child in current_process.children(recursive=True): try: mem += child.memory_info().rss - except Exception: - pass + except psutil.Error: + continue return mem diff --git a/pyproject.toml b/pyproject.toml index b8d939d0..6e8e0358 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -102,7 +102,7 @@ dev = [ "pytest-cov>=5.0.0", "pre-commit>=3.7.1", "pyright>=1.1.373", - "ruff>=0.5.5", + "ruff>0.16,<0.17", # Diagnostics "psutil>=6.0.0", "ipython>=8.18.1", @@ -136,6 +136,7 @@ target-version = "py310" # Minimum supported version line-length = 88 exclude = [ "docs", + "pooltool/utils/strenum.py", # Vendored StrEnum backport, kept byte-faithful to upstream ] [tool.ruff.lint] @@ -145,6 +146,7 @@ extend-select = [ ] ignore = [ "E741", # Ambiguous variable name + "PLR0402", # The architecture relies on `import pooltool.x as x` submodule aliasing ] [tool.pytest.ini_options] diff --git a/sandbox/airborne_demos.py b/sandbox/airborne_demos.py old mode 100644 new mode 100755 diff --git a/sandbox/arena.py b/sandbox/arena.py old mode 100644 new mode 100755 index c866ccd7..8e8ce408 --- a/sandbox/arena.py +++ b/sandbox/arena.py @@ -5,7 +5,7 @@ import pooltool as pt -get_pos = lambda table, ball: ( # noqa E731 +get_pos = lambda table, ball: ( (table.w - 2 * ball.params.R) * np.random.rand() + ball.params.R, (table.l - 2 * ball.params.R) * np.random.rand() + ball.params.R, ball.params.R, diff --git a/sandbox/ball_ball_collisions.py b/sandbox/ball_ball_collisions.py index b718bb16..ba160e72 100644 --- a/sandbox/ball_ball_collisions.py +++ b/sandbox/ball_ball_collisions.py @@ -148,7 +148,7 @@ def collision_results_versus_cut_angle( vel[i] = collision_experiment.cb_i.vel avel[i] = collision_experiment.cb_i.avel - cb_f, ob_f = collision_experiment.result() + _cb_f, ob_f = collision_experiment.result() induced_vel[i] = ptmath.coordinate_rotation( ob_f.vel, -config.xy_line_of_centers_angle_radians @@ -202,7 +202,7 @@ def collision_results_versus_sidespin( vel[i] = collision_experiment.cb_i.vel avel[i] = collision_experiment.cb_i.avel - cb_f, ob_f = collision_experiment.result() + _cb_f, ob_f = collision_experiment.result() induced_vel[i] = ptmath.coordinate_rotation( ob_f.vel, -config.xy_line_of_centers_angle_radians diff --git a/sandbox/ball_cushion_collisions.py b/sandbox/ball_cushion_collisions.py old mode 100644 new mode 100755 index efdf0f5b..78f21fdc --- a/sandbox/ball_cushion_collisions.py +++ b/sandbox/ball_cushion_collisions.py @@ -239,7 +239,7 @@ def plot_rebound_angle_vs_incident_angle( y=cut_angles_deg, mode="lines", name="1:1 line (perfect reflection)", - line=dict(color="gray", width=1, dash="dash"), + line={"color": "gray", "width": 1, "dash": "dash"}, opacity=0.7, ) ) @@ -252,17 +252,12 @@ def plot_rebound_angle_vs_incident_angle( ) base_color = base_colors[config_idx % len(base_colors)] - trajectory_idx = 0 num_trajectories = len(results) - for (speed, topspin_factor, sidespin_factor), ( - _, - _, - _, - _, - rebound_angles, - _, - ) in results.items(): + for trajectory_idx, ( + (speed, topspin_factor, sidespin_factor), + (_, _, _, _, rebound_angles, _), + ) in enumerate(results.items()): label = f"{config.model.model}: speed={speed:.3} m/s" if topspin_factors is not None: label += f", topspin_factor={topspin_factor:.2}" @@ -278,11 +273,10 @@ def plot_rebound_angle_vs_incident_angle( y=rebound_angles_deg, mode="lines", name=label, - line=dict(color=base_color, width=2), + line={"color": base_color, "width": 2}, opacity=opacity, ) ) - trajectory_idx += 1 fig.update_layout( title=title, @@ -309,17 +303,12 @@ def plot_rebound_speed_vs_incident_angle( ) base_color = base_colors[config_idx % len(base_colors)] - trajectory_idx = 0 num_trajectories = len(results) - for (speed, topspin_factor, sidespin_factor), ( - _, - _, - _, - _, - _, - rebound_speeds, - ) in results.items(): + for trajectory_idx, ( + (speed, topspin_factor, sidespin_factor), + (_, _, _, _, _, rebound_speeds), + ) in enumerate(results.items()): label = f"{config.model.model}: speed={speed:.3} m/s" if topspin_factors is not None: label += f", topspin_factor={topspin_factor:.2}" @@ -334,11 +323,10 @@ def plot_rebound_speed_vs_incident_angle( y=rebound_speeds, mode="lines", name=label, - line=dict(color=base_color, width=2), + line={"color": base_color, "width": 2}, opacity=opacity, ) ) - trajectory_idx += 1 fig.update_layout( title=title, @@ -369,17 +357,12 @@ def plot_change_in_angular_velocity_vs_incident_angle( ) base_color = base_colors[config_idx % len(base_colors)] - trajectory_idx = 0 num_trajectories = len(results) - for (speed, topspin_factor, sidespin_factor), ( - _, - avel, - _, - outgoing_avel, - _, - _, - ) in results.items(): + for trajectory_idx, ( + (speed, topspin_factor, sidespin_factor), + (_, avel, _, outgoing_avel, _, _), + ) in enumerate(results.items()): label = f"{config.model.model}: speed={speed:.3} m/s" if topspin_factors is not None: label += f", topspin_factor={topspin_factor:.2}" @@ -398,13 +381,11 @@ def plot_change_in_angular_velocity_vs_incident_angle( y=100 * outgoing_avel_proportion_of_avel, mode="lines", name=label, - line=dict(color=base_color, width=2), + line={"color": base_color, "width": 2}, opacity=opacity, ) ) - trajectory_idx += 1 - fig.update_layout( title=title, xaxis_title="incident angle (deg)", diff --git a/sandbox/break_forever.py b/sandbox/break_forever.py old mode 100644 new mode 100755 diff --git a/sandbox/collection.py b/sandbox/collection.py old mode 100644 new mode 100755 diff --git a/sandbox/continuize_and_render_timer.py b/sandbox/continuize_and_render_timer.py old mode 100644 new mode 100755 diff --git a/sandbox/custom_table.py b/sandbox/custom_table.py old mode 100644 new mode 100755 diff --git a/sandbox/load.py b/sandbox/load.py old mode 100644 new mode 100755 diff --git a/sandbox/offscreen/offscreen.py b/sandbox/offscreen/offscreen.py old mode 100644 new mode 100755 diff --git a/sandbox/offscreen/timing.py b/sandbox/offscreen/timing.py old mode 100644 new mode 100755 diff --git a/tests/objects/ball/test_datatypes.py b/tests/objects/ball/test_datatypes.py index dea3ad93..7d6e2786 100644 --- a/tests/objects/ball/test_datatypes.py +++ b/tests/objects/ball/test_datatypes.py @@ -98,8 +98,8 @@ def test_ball_history_equality(): history1 = BallHistory(states=[state1]) history2 = BallHistory(states=[state2]) - assert history1 == history1 - assert history2 == history2 + assert history1 == history1 # noqa: PLR0124 + assert history2 == history2 # noqa: PLR0124 assert history1 != history2 diff --git a/tests/objects/table/test_components.py b/tests/objects/table/test_components.py index fde63e8b..0aa5548c 100644 --- a/tests/objects/table/test_components.py +++ b/tests/objects/table/test_components.py @@ -163,7 +163,7 @@ def test_pocket_copy(pocket): assert pocket == copy # center is read only, so its safe that they share the same reference - pocket.center is copy.center # type: ignore + assert pocket.center is copy.center # contains is mutable, so separate objects is necessary assert pocket.contains == copy.contains diff --git a/tests/physics/resolve/ball_ball/test_ball_ball.py b/tests/physics/resolve/ball_ball/test_ball_ball.py index 4e30f6db..1d2c4354 100644 --- a/tests/physics/resolve/ball_ball/test_ball_ball.py +++ b/tests/physics/resolve/ball_ball/test_ball_ball.py @@ -80,7 +80,7 @@ def translating_head_on() -> tuple[Ball, Ball]: @pytest.mark.parametrize("model", [FrictionlessElastic()]) def test_head_on_zero_spin(model: BallBallCollisionStrategy): cb_i, ob_i = head_on() - cb_f, ob_f = model.resolve(cb_i, ob_i, inplace=False) + cb_f, _ob_f = model.resolve(cb_i, ob_i, inplace=False) # Since balls are of equal mass, final cue ball +x speed should not be positive assert cb_f.state.rvw[1][0] <= 0 @@ -193,7 +193,7 @@ def test_gearing_z_spin( v_c = tangent_surface_velocity(cb_i.state.rvw, unit_normal, cb_i.params.R) assert ptmath.norm3d(v_c) < 1e-10, "Relative surface contact speed should be zero" - cb_f, ob_f = model.resolve(cb_i, ob_i, inplace=False) + _cb_f, ob_f = model.resolve(cb_i, ob_i, inplace=False) assert np.allclose( np.cross(ob_f.vel, unit_normal), np.zeros_like(unit_normal), atol=1e-3 diff --git a/tests/ptmath/roots/data/_speed_comparison.py b/tests/ptmath/roots/data/_speed_comparison.py index 3af97226..e4103dbb 100644 --- a/tests/ptmath/roots/data/_speed_comparison.py +++ b/tests/ptmath/roots/data/_speed_comparison.py @@ -77,7 +77,9 @@ def main(): print(f"Numba: {numba_mean * 1e6:8.4f} μs ± {numba_std * 1e6:6.4f} μs") benchmark_path = Path(__file__).parent / "_1010_source_code" / "benchmark" - result = subprocess.run([str(benchmark_path)], capture_output=True, text=True) + result = subprocess.run( + [str(benchmark_path)], capture_output=True, text=True, check=True + ) print(result.stdout.strip()) diff --git a/tests/ptmath/test_ptmath.py b/tests/ptmath/test_ptmath.py index 8e432a78..2cce89f9 100644 --- a/tests/ptmath/test_ptmath.py +++ b/tests/ptmath/test_ptmath.py @@ -45,19 +45,19 @@ def test_are_points_on_same_side(): def test_transcendental_linear_equation(): - f = lambda x: x - 5 # noqa E731 + f = lambda x: x - 5 root = solve_transcendental(f, 0, 10) assert pytest.approx(root, 0.00001) == 5.0 def test_transcendental_nonlinear_equation(): - f = lambda x: x**2 - 4 * x + 3 # noqa E731 + f = lambda x: x**2 - 4 * x + 3 root = solve_transcendental(f, 0, 2.5) assert pytest.approx(root, 0.00001) == 1.0 def test_transcendental_no_root_error(): - f = lambda x: x**2 + 1 # noqa E731 + f = lambda x: x**2 + 1 with pytest.raises(ValueError): solve_transcendental(f, 0, 10) diff --git a/uv.lock b/uv.lock index 83d45449..31165715 100644 --- a/uv.lock +++ b/uv.lock @@ -382,7 +382,7 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform != 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } wheels = [ @@ -459,7 +459,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } wheels = [ @@ -662,7 +662,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -926,17 +926,17 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform != 'darwin'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "exceptiongroup" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, - { name = "typing-extensions" }, + { name = "colorama", marker = "python_full_version < '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version < '3.11'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.11'" }, + { name = "jedi", marker = "python_full_version < '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version < '3.11'" }, + { name = "pexpect", marker = "python_full_version < '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version < '3.11'" }, + { name = "pygments", marker = "python_full_version < '3.11'" }, + { name = "stack-data", marker = "python_full_version < '3.11'" }, + { name = "traitlets", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/40/18/f8598d287006885e7136451fdea0755af4ebcbfe342836f24deefaed1164/ipython-8.39.0.tar.gz", hash = "sha256:4110ae96012c379b8b6db898a07e186c40a2a1ef5d57a7fa83166047d9da7624", size = 5513971, upload-time = "2026-03-27T10:02:13.94Z" } wheels = [ @@ -958,18 +958,18 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "colorama", marker = "sys_platform == 'win32'" }, - { name = "decorator" }, - { name = "ipython-pygments-lexers" }, - { name = "jedi" }, - { name = "matplotlib-inline" }, - { name = "pexpect", marker = "sys_platform != 'emscripten' and sys_platform != 'win32'" }, - { name = "prompt-toolkit" }, - { name = "psutil", marker = "sys_platform != 'cygwin' and sys_platform != 'emscripten'" }, - { name = "pygments" }, - { name = "stack-data" }, - { name = "traitlets" }, - { name = "typing-extensions", marker = "python_full_version < '3.12'" }, + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version >= '3.11'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, + { name = "jedi", marker = "python_full_version >= '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, + { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, + { name = "psutil", marker = "python_full_version >= '3.11' and sys_platform != 'cygwin' and sys_platform != 'emscripten'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "stack-data", marker = "python_full_version >= '3.11'" }, + { name = "traitlets", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/53/59/165d3b4d75cc34add3122c4417ecb229085140ac573103c223cd01dde96f/ipython-9.15.0.tar.gz", hash = "sha256:da2819ce2aa83135257df830660b1176d986c3d2876db24df01974fa955b2756", size = 4442580, upload-time = "2026-06-26T11:03:35.913Z" } wheels = [ @@ -981,7 +981,7 @@ name = "ipython-pygments-lexers" version = "1.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "pygments" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } wheels = [ @@ -1469,15 +1469,15 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform != 'darwin'", ] dependencies = [ - { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" } }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "cycler", marker = "python_full_version < '3.11'" }, + { name = "fonttools", marker = "python_full_version < '3.11'" }, + { name = "kiwisolver", marker = "python_full_version < '3.11'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "pillow", marker = "python_full_version < '3.11'" }, + { name = "pyparsing", marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/63/1b/4be5be87d43d327a0cf4de1a56e86f7f84c89312452406cf122efe2839e6/matplotlib-3.10.9.tar.gz", hash = "sha256:fd66508e8c6877d98e586654b608a0456db8d7e8a546eb1e2600efd957302358", size = 34811233, upload-time = "2026-04-24T00:14:13.539Z" } wheels = [ @@ -1538,15 +1538,15 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" } }, - { name = "cycler" }, - { name = "fonttools" }, - { name = "kiwisolver" }, - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" } }, - { name = "packaging" }, - { name = "pillow" }, - { name = "pyparsing" }, - { name = "python-dateutil" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "cycler", marker = "python_full_version >= '3.11'" }, + { name = "fonttools", marker = "python_full_version >= '3.11'" }, + { name = "kiwisolver", marker = "python_full_version >= '3.11'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pillow", marker = "python_full_version >= '3.11'" }, + { name = "pyparsing", marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/1f/24/080c99d223d158d3a8902769269ab6da5b50f7a0e6e072513907e02b7a6c/matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57", size = 33251176, upload-time = "2026-06-12T02:29:15.508Z" } wheels = [ @@ -2136,10 +2136,10 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform != 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, - { name = "python-dateutil" }, - { name = "pytz" }, - { name = "tzdata" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "python-dateutil", marker = "python_full_version < '3.11'" }, + { name = "pytz", marker = "python_full_version < '3.11'" }, + { name = "tzdata", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/33/01/d40b85317f86cf08d853a4f495195c73815fdf205eef3993821720274518/pandas-2.3.3.tar.gz", hash = "sha256:e05e1af93b977f7eafa636d043f9f94c7ee3ac81af99c13508215942e64c993b", size = 4495223, upload-time = "2025-09-29T23:34:51.853Z" } wheels = [ @@ -2194,9 +2194,9 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" } }, - { name = "python-dateutil" }, - { name = "tzdata", marker = "sys_platform == 'emscripten' or sys_platform == 'win32'" }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.11'" }, + { name = "tzdata", marker = "(python_full_version >= '3.11' and sys_platform == 'emscripten') or (python_full_version >= '3.11' and sys_platform == 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5a/fd/e0194474c71dbfba82e744f66945c274f15b667acd5f8c117b12555fb91e/pandas-3.0.4.tar.gz", hash = "sha256:62f6062586d159663825f06e70ef49cd1572d45824cb63a9559f3ffd1d0d2a20", size = 4658146, upload-time = "2026-06-28T15:31:51.3Z" } wheels = [ @@ -2250,7 +2250,7 @@ name = "pexpect" version = "4.9.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "ptyprocess" }, + { name = "ptyprocess", marker = "(python_full_version < '3.11' and sys_platform == 'emscripten') or (python_full_version < '3.11' and sys_platform == 'win32') or (sys_platform != 'emscripten' and sys_platform != 'win32')" }, ] sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } wheels = [ @@ -2438,7 +2438,7 @@ dev = [ { name = "pyright", specifier = ">=1.1.373" }, { name = "pytest", specifier = ">=8.3.2" }, { name = "pytest-cov", specifier = ">=5.0.0" }, - { name = "ruff", specifier = ">=0.5.5" }, + { name = "ruff", specifier = ">0.16,<0.17" }, ] docs = [ { name = "astroid", specifier = "==3.3.11" }, @@ -3042,27 +3042,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.20" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/dc/35b341fc554ba02f217fc10da57d1a75168cfbcf75b0ef2202176d4c4f2d/ruff-0.15.20.tar.gz", hash = "sha256:1416eb04349192646b54de98f146c4f59afe37d0decfc02c3cbbf396f3a28566", size = 4755489, upload-time = "2026-06-25T17:20:37.578Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/94/d9/2d5014f0253ba541d2061d9fa7193f48e941c8b21bb88a7ff9bbe0bd0596/ruff-0.15.20-py3-none-linux_armv6l.whl", hash = "sha256:00e188c53e499c3c1637f73c91dcf2fb56d576cab76ce1be50a27c4e80e37078", size = 10839665, upload-time = "2026-06-25T17:19:44.702Z" }, - { url = "https://files.pythonhosted.org/packages/c6/d3/ac1798ba64f670698867fcfc591d50e7e421bef137db564858f619a30fcf/ruff-0.15.20-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:9ebd1fd9b9c95fc0bd7b2761aebec1f030013d2e193a2901b224af68fe47251b", size = 11208649, upload-time = "2026-06-25T17:19:48.787Z" }, - { url = "https://files.pythonhosted.org/packages/47/47/d3ac899991202095dfcf3d5176be4272642be3cf981a2f1a30f72a2afb95/ruff-0.15.20-py3-none-macosx_11_0_arm64.whl", hash = "sha256:c5b16cdd67ca108185cd36dce98c576350c03b1660a751de725fb049193a0632", size = 10622638, upload-time = "2026-06-25T17:19:51.354Z" }, - { url = "https://files.pythonhosted.org/packages/33/13/4e043fe30aa94d4ff5213a9881fc296d12960f5971b234a5263fdc225312/ruff-0.15.20-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3413bb3c3d2ca6a8208f1f4809cd2dca3c6de6d0b491c0e70847672bde6e6efd", size = 10984227, upload-time = "2026-06-25T17:19:54.044Z" }, - { url = "https://files.pythonhosted.org/packages/76/e6/92e7bf40388bc5800073b96564f56264f7e48bfd1a498f5ced6ae6d5a769/ruff-0.15.20-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bd7ec42b3bb3da066488db093308a69c4ac5ee6d2af333a86ba6e2eb2e7dd44b", size = 10622882, upload-time = "2026-06-25T17:19:57.037Z" }, - { url = "https://files.pythonhosted.org/packages/13/7a/43460be3f24495a3aa46d4b16873e2c4941b3b5f0b00cf88c03b7b94b339/ruff-0.15.20-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1a36ad0eb77fba9aabfb69ede54de6f376d04ac18ebea022847046d340a8267", size = 11474808, upload-time = "2026-06-25T17:20:00.357Z" }, - { url = "https://files.pythonhosted.org/packages/27/a0/f37077884873221c6b33b4ab49eb18f9f88e54a16a25a5bca59bef46dd66/ruff-0.15.20-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b6df3b1e4610432f0386dba04d853b5f08cbbc903410c6fcc02f620f05aff53c", size = 12293094, upload-time = "2026-06-25T17:20:03.446Z" }, - { url = "https://files.pythonhosted.org/packages/a6/74/165545b60256a9704c21ac0ec4a0d07933b320812f9584836c9f4aca4292/ruff-0.15.20-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e89f198a1ea6ef0d727c1cf16088bc91a6cb0ab947dedc966715691647186eae", size = 11526176, upload-time = "2026-06-25T17:20:06.301Z" }, - { url = "https://files.pythonhosted.org/packages/86/b1/a976a136d40ade83ce743578399865f57001003a409acadc0ecbb3051082/ruff-0.15.20-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:309809086c2acb67624950a3c8133e80f32d0d3e27106c0cd60ff26657c9f24b", size = 11520767, upload-time = "2026-06-25T17:20:09.191Z" }, - { url = "https://files.pythonhosted.org/packages/19/0f/f032696cb01c9b54c0263fa393474d7758f1cdc021a01b04e3cbc2500999/ruff-0.15.20-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:2d2374caa2f2c2f9e2b7da0a50802cfb8b79f55a9b5e49379f564544fbf56487", size = 11500132, upload-time = "2026-06-25T17:20:13.602Z" }, - { url = "https://files.pythonhosted.org/packages/4b/f4/51b1a14bc69e8c224b15dab9cce8e99b425e0455d462caa2b3c9be2b6a8e/ruff-0.15.20-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:a1ed17b65293e0c2f22fc387bc13198a5de94bf4429589b0ff6946b0feaf21a3", size = 10943828, upload-time = "2026-06-25T17:20:16.635Z" }, - { url = "https://files.pythonhosted.org/packages/71/4b/fe267640783cd02bf6c5cc290b1df1051be2ec294c678b5c15fe19e52343/ruff-0.15.20-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:f701305e66b38ea6c91882490eb73459796808e4c6362a1b765255e0cdcd4053", size = 10645418, upload-time = "2026-06-25T17:20:19.4Z" }, - { url = "https://files.pythonhosted.org/packages/b0/c0/a65aa4ec2f5e87a1df32dc3ec1fede434fe3dfd5cbcf3b503cafc676ab54/ruff-0.15.20-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5b9c0c367ad8e5d0d5b5b8537864c469a0a0e55417aadfbeca41fa61333be9f4", size = 11211770, upload-time = "2026-06-25T17:20:22.033Z" }, - { url = "https://files.pythonhosted.org/packages/5a/a4/0caa331d954ae2723d729d351c989cb4ca8b6077d5c6c2cb6de75e98c041/ruff-0.15.20-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:01cc00dd58f0df339d0e902219dd53990ea99996a0344e5d9cc8d45d5307e460", size = 11618698, upload-time = "2026-06-25T17:20:25.259Z" }, - { url = "https://files.pythonhosted.org/packages/10/9b/5f14927848d2fd4aa891fd88d883788c5a7baba561c7874732364045708c/ruff-0.15.20-py3-none-win32.whl", hash = "sha256:ed65ef510e43a137207e0f01cfcf998aeddb1aeeda5c9d35023e910284d7cf21", size = 10857322, upload-time = "2026-06-25T17:20:28.612Z" }, - { url = "https://files.pythonhosted.org/packages/fa/f0/fe47c501f9dea92a26d788ff98bb5d92ed4cb4c88792c5c88af6b697dc8e/ruff-0.15.20-py3-none-win_amd64.whl", hash = "sha256:a525c81c70fb0380344dd1d8745d8cc1c890b7fc94a58d5a07bd8eb9557b8415", size = 11993274, upload-time = "2026-06-25T17:20:31.871Z" }, - { url = "https://files.pythonhosted.org/packages/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" }, +version = "0.16.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/70/25/7113f6d5498888c5fb7db34081cba7d5971c4cb1bfb26819966eee68f003/ruff-0.16.1.tar.gz", hash = "sha256:fedad7c801dabd3fb9741d76aca39246e6ddd9ca446a015875207bf19f1e6bc7", size = 4877500, upload-time = "2026-07-30T19:37:01.379Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1b/bd/694da69368e0973de65df2ddc73ab18d43c469d5963d9b150911de6bc513/ruff-0.16.1-py3-none-linux_armv6l.whl", hash = "sha256:58edb313b88f0c5460a26adf5f39a37a3be789494a15e3e411e35fa78b89f9a0", size = 10839126, upload-time = "2026-07-30T19:36:13.697Z" }, + { url = "https://files.pythonhosted.org/packages/3f/f0/b626e5d5bd0dd9576263658ef12885e2288afd1029a48e26ffed65ec1ac1/ruff-0.16.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:fde5a99e2f97479af66edd6622c6d5a2a7592c77cf4153d9e4428f5eeb55b60c", size = 11070253, upload-time = "2026-07-30T19:36:17.14Z" }, + { url = "https://files.pythonhosted.org/packages/83/63/f40acfb6b35b88623e71684942b552c3edd96035f5d98f313815f7b277de/ruff-0.16.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e0d4c20532fca4f7fa609369161d968dd28f65d83dabbd61d8e9c7edbf7001f6", size = 10561425, upload-time = "2026-07-30T19:36:20.04Z" }, + { url = "https://files.pythonhosted.org/packages/aa/dd/14ec0e9c2b4d315547dd38765004b4863e354e1b52cb308272215d9f6f6d/ruff-0.16.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30affbcedf59ad5703d9c91f82266e02b47739f797e1a7b6e158e5526a6dae38", size = 10948879, upload-time = "2026-07-30T19:36:22.476Z" }, + { url = "https://files.pythonhosted.org/packages/33/e9/9d870cbae575030fdef595f04b4b97573c525b5497cce4f4498cf2f85446/ruff-0.16.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:24e9c631573cbca9d20f1283f8f479b2afa4a8503504822bd71a293889f16743", size = 10643691, upload-time = "2026-07-30T19:36:24.914Z" }, + { url = "https://files.pythonhosted.org/packages/c4/09/12743d544e2173f53ecd27217c65f90d2bc0f8424a66a60339e56bbc0457/ruff-0.16.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b41bdd48fb420987a9b5212e4957c26ad4abce401fa9ea9d4d85843727945f4f", size = 11435354, upload-time = "2026-07-30T19:36:28.447Z" }, + { url = "https://files.pythonhosted.org/packages/7f/89/a1652b2daee52083c9554a6333b678a8b01d0400f976827bb87857f9449a/ruff-0.16.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b0d1e1393b7648079e13669de1c1f4fde06d4583e84d8fd5c1551e0a77a2aa75", size = 12259033, upload-time = "2026-07-30T19:36:31.326Z" }, + { url = "https://files.pythonhosted.org/packages/16/96/ecdcb8c54ee7b123b487f807eb014e6e019155a0b81dfb669acd52f28ce3/ruff-0.16.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:07bf434b1c95f4e093be4532068ef4fcf00924eb2ade8796075980902d6fd54a", size = 11667981, upload-time = "2026-07-30T19:36:34.394Z" }, + { url = "https://files.pythonhosted.org/packages/cd/90/c52e12e0d862e9572f2a33aa227409143520abe53111e9a6babbac7b4af8/ruff-0.16.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39897739f112253ee4fdd2e8aa9a4f9ded99fb2be367d5f31dfa4ded6025584c", size = 11468183, upload-time = "2026-07-30T19:36:37.339Z" }, + { url = "https://files.pythonhosted.org/packages/2c/6b/4ffb7ad1d83eb16cf8cbb3c8815d3f11c88460fd162d4b372a2059be1c2a/ruff-0.16.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:82ae3c0c0d74daf17b968a10b7b3bb3ef297ab7de0c1f749646b25e690ccb150", size = 11470071, upload-time = "2026-07-30T19:36:39.91Z" }, + { url = "https://files.pythonhosted.org/packages/9c/72/32ae7db4c0b5e32ab611787caa19d1546800676d79f7483b7100a3561bf4/ruff-0.16.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4d5f2ed10f8242d83fc08d521301089364e3375375705356f20c0e31606ef3ef", size = 10919503, upload-time = "2026-07-30T19:36:42.65Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ca/3d901ba6ad6fc38da39c3448fc6c59ac945679293a17c3ceb6d6c1cba13e/ruff-0.16.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a4665b309891f83f3e3c25447935f1213e9abbd4b5640af7a1f2def9f8d413c1", size = 10649861, upload-time = "2026-07-30T19:36:45.18Z" }, + { url = "https://files.pythonhosted.org/packages/92/79/894ef1ced26552d5f8c9cf6d85b0687840e1128c55aeab7b9c2d54a0d880/ruff-0.16.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:26e9ca5c9bc3971f20d3cf18a957f52ffd6a5f6564ff15c4912a144dcac22494", size = 11148137, upload-time = "2026-07-30T19:36:47.936Z" }, + { url = "https://files.pythonhosted.org/packages/2d/69/3609a09fa1cb46cc28b762363e440a354204e5dff01bd0c8d7437874d6b9/ruff-0.16.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:67e1e1e3fa4f0c82f0e36d4cd61e661f6e7a6196cb1aa92fe0828fa7b8f257cd", size = 11559211, upload-time = "2026-07-30T19:36:50.448Z" }, + { url = "https://files.pythonhosted.org/packages/fc/8a/fb22af2fd78a736e241fabf67e30ce1799a64244026377a49e133af90762/ruff-0.16.1-py3-none-win32.whl", hash = "sha256:d31765e131295b8445caf301e3e8a85b34d1b9b211b4109b7ba457888b051806", size = 10838258, upload-time = "2026-07-30T19:36:53.298Z" }, + { url = "https://files.pythonhosted.org/packages/d4/35/e57fd9fb5d423961df087a00b12d42c0a830288dc2f3b45ecca299158b4f/ruff-0.16.1-py3-none-win_amd64.whl", hash = "sha256:09b05e8b90c2cb06ad63464350e7a45e8e44a2dfe52072ebfba6666ca8d3f596", size = 11961111, upload-time = "2026-07-30T19:36:56.107Z" }, + { url = "https://files.pythonhosted.org/packages/cb/46/240ea004bf6dc4feb40e9832f2205a476a47dd5b8a3f8211a5fc5f95e20e/ruff-0.16.1-py3-none-win_arm64.whl", hash = "sha256:dbaadaac38c70239f056d306b7476f246b0bf000fa6b3876402acbf5b227eaf8", size = 11309414, upload-time = "2026-07-30T19:36:58.79Z" }, ] [[package]] @@ -3074,7 +3074,7 @@ resolution-markers = [ "python_full_version < '3.11' and sys_platform != 'darwin'", ] dependencies = [ - { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/0f/37/6964b830433e654ec7485e45a00fc9a27cf868d622838f6b6d9c5ec0d532/scipy-1.15.3.tar.gz", hash = "sha256:eae3cf522bc7df64b42cad3925c876e1b0b6c35c1337c93e12c0f366f55b0eaf", size = 59419214, upload-time = "2025-05-08T16:13:05.955Z" } wheels = [ @@ -3136,7 +3136,7 @@ resolution-markers = [ "python_full_version == '3.11.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.11.*'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" } wheels = [ @@ -3193,7 +3193,7 @@ resolution-markers = [ "python_full_version >= '3.12' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'", ] dependencies = [ - { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" } }, + { name = "numpy", version = "2.3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" } wheels = [