diff --git a/.cirrus.yml b/.cirrus.yml index 8a8b3fc9..783662a1 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -12,6 +12,22 @@ docs_task: - make -C docs/ html +lint_task: + skip: $CIRRUS_BRANCH =~ '.*\.tmp' + container: + image: python:3.7-slim + + install_script: + - pip install --upgrade-strategy eager -U -r requirements-tests.txt + - pip install --upgrade-strategy eager -U -r requirements-lint.txt + - pip install -e . + + script: + - python3 --version + - pip list + - ./lint.sh + + task: name: "Linux $IMAGE" skip: $CIRRUS_BRANCH =~ '.*\.tmp' diff --git a/.common.sh b/.common.sh new file mode 100644 index 00000000..02b29f92 --- /dev/null +++ b/.common.sh @@ -0,0 +1,14 @@ +set -euo pipefail + +function run() { + echo '$' "$@" + "$@" + echo +} + +PY=${PY-python3} + +if ! command -v $PY >/dev/null; then + echo "Python interpreter '$PY' not found" >&2 + exit 1 +fi diff --git a/docs/conf.py b/docs/conf.py index 3b7e483f..8a189c14 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -142,7 +142,7 @@ # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'pursuedpybear', 'PursuedPyBear Documentation', - [author], 1) + [author], 1), ] @@ -181,4 +181,4 @@ # -- Options for todo extension ---------------------------------------------- # If true, `todo` and `todoList` produce output, else they produce nothing. -todo_include_todos = True \ No newline at end of file +todo_include_todos = True diff --git a/examples/animated_sprite.py b/examples/animated_sprite.py index 81fd7e5c..b5aea0da 100644 --- a/examples/animated_sprite.py +++ b/examples/animated_sprite.py @@ -1,8 +1,8 @@ import math import ppb -from ppb.features.animation import Animation import ppb.events as events +from ppb.features.animation import Animation class Blob(ppb.BaseSprite): diff --git a/examples/framecount.py b/examples/framecount.py index 9560b6fa..b9d6e728 100644 --- a/examples/framecount.py +++ b/examples/framecount.py @@ -8,6 +8,7 @@ import logging import time + import ppb diff --git a/examples/targets.py b/examples/targets.py index d5eff598..acf4cddf 100644 --- a/examples/targets.py +++ b/examples/targets.py @@ -1,7 +1,7 @@ import logging + import ppb -from ppb import Vector -from ppb import keycodes +from ppb import keycodes, Vector class MoverMixin(ppb.BaseSprite): @@ -39,7 +39,7 @@ def on_button_pressed(self, event, signal): def _fire_bullet(self, scene): scene.add( Bullet(pos=self.position), - tags=['bullet'] + tags=['bullet'], ) @@ -51,7 +51,7 @@ def on_update(self, update, signal): super().on_update(update, signal) # Execute movement scene = update.scene - + if self.position.y > scene.main_camera.frame_bottom: scene.remove(self) else: diff --git a/examples/targets_with_twisted.py b/examples/targets_with_twisted.py index 596e723b..9016fcd0 100644 --- a/examples/targets_with_twisted.py +++ b/examples/targets_with_twisted.py @@ -1,15 +1,13 @@ -import logging -import ppb -from ppb import Vector -from ppb import keycodes -from twisted.internet import defer -from twisted.internet import task -from twisted.internet import endpoints -from twisted.web.server import Site -import klein from dataclasses import dataclass from typing import Any +import klein +from twisted.internet import defer, endpoints, task +from twisted.web.server import Site + +import ppb +from ppb import keycodes, Vector + class MoverMixin(ppb.BaseSprite): velocity = Vector(0, 0) @@ -45,7 +43,7 @@ def on_button_pressed(self, event, signal): def _fire_bullet(self, scene): scene.add( Bullet(pos=self.position), - tags=['bullet'] + tags=['bullet'], ) @@ -74,7 +72,7 @@ def on_update(self, update, signal): super().on_update(update, signal) # Execute movement scene = update.scene - + if self.position.y > scene.main_camera.frame_bottom: scene.remove(self) else: @@ -102,7 +100,7 @@ def __init__(self, *p, **kw): self.add(Target(pos=Vector(x, 1.875)), tags=['target']) -######### This is "non-game-specific code" ########### +# This is "non-game-specific code" class _FinishLoop(Exception): pass @@ -119,7 +117,7 @@ def loop_once(engine): yield loop.start(0.001) except _FinishLoop: pass -######### End of "non-game-specific code" ########### +# End of "non-game-specific code" @defer.inlineCallbacks @@ -128,10 +126,11 @@ def main(reactor): TargetCounter.web_server( reactor=reactor, engine=engine, - description="tcp:8080" + description="tcp:8080", ) yield twisted_engine_loop(engine) + if __name__ == "__main__": import sys task.react(main, sys.argv[1:]) diff --git a/lint.sh b/lint.sh new file mode 100755 index 00000000..b8a23f71 --- /dev/null +++ b/lint.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +source .common.sh + +run flake8 --version +run flake8 diff --git a/ppb/__init__.py b/ppb/__init__.py index 0ec0774a..47474282 100644 --- a/ppb/__init__.py +++ b/ppb/__init__.py @@ -2,7 +2,7 @@ from typing import Callable from ppb_vector import Vector -from ppb.engine import GameEngine +from ppb.engine import GameEngine # noqa: I100 from ppb.scenes import BaseScene from ppb.sprites import BaseSprite @@ -25,7 +25,8 @@ def _make_kwargs(setup, title): } return kwargs -def run(setup: Callable[[BaseScene], None]=None, *, log_level=logging.WARNING, + +def run(setup: Callable[[BaseScene], None] = None, *, log_level=logging.WARNING, starting_scene=BaseScene, title="PursuedPyBear"): """ Run a small game. @@ -45,6 +46,6 @@ def run(setup: Callable[[BaseScene], None]=None, *, log_level=logging.WARNING, eng.run() -def make_engine(setup: Callable[[BaseScene], None]=None, *, +def make_engine(setup: Callable[[BaseScene], None] = None, *, starting_scene=BaseScene, title="PursedPyBear"): return GameEngine(starting_scene, **_make_kwargs(setup, title)) diff --git a/ppb/abc.py b/ppb/abc.py index 366dff96..439fd64b 100644 --- a/ppb/abc.py +++ b/ppb/abc.py @@ -1,4 +1,4 @@ -from typing import Type, Container, Tuple, Iterable +from typing import Iterable, Tuple, Type class Engine(object): diff --git a/ppb/camera.py b/ppb/camera.py index 6a9d58ee..355356e7 100644 --- a/ppb/camera.py +++ b/ppb/camera.py @@ -1,17 +1,17 @@ -from typing import Sequence -from typing import Union from numbers import Number +from typing import Sequence from ppb import Vector -from ppb.sprites import BaseSprite from ppb.flags import DoNotRender +from ppb.sprites import BaseSprite + class Camera(BaseSprite): image = DoNotRender - def __init__(self, viewport: Sequence[int]=(0, 0, 800, 600), - pixel_ratio: float=64): + def __init__(self, viewport: Sequence[int] = (0, 0, 800, 600), + pixel_ratio: float = 64): """ viewport: A container of origin x, origin y, width, and @@ -64,6 +64,7 @@ def half_height(self) -> float: @property def half_width(self) -> float: return self.frame_width / 2 + @property def viewport_width(self) -> int: return self._viewport_width @@ -82,25 +83,26 @@ def viewport_height(self, value: int): self._viewport_height = value self.viewport_offset = Vector(self.viewport_width / 2, value / 2) - def point_in_viewport(self, point:Vector) -> bool: + def point_in_viewport(self, point: Vector) -> bool: px, py = point vpx, vpy = self.viewport_origin vpw = self.viewport_width vph = self.viewport_height - return vpx <= px <= (vpw+vpx) and vpy <= py <= (vph+vpy) + return vpx <= px <= (vpw + vpx) and vpy <= py <= (vph + vpy) def in_frame(self, sprite: BaseSprite) -> bool: - return (self.frame_left <= sprite.right and - self.frame_right >= sprite.left and - self.frame_top <= sprite.bottom and - self.frame_bottom >= sprite.top - ) + return ( + self.frame_left <= sprite.right and + self.frame_right >= sprite.left and + self.frame_top <= sprite.bottom and + self.frame_bottom >= sprite.top + ) def translate_to_frame(self, point: Vector) -> Vector: """ Converts a vector from pixel-based window to in-game coordinate space """ - offset = (point - self.viewport_offset) * (1/self.pixel_ratio) + offset = (point - self.viewport_offset) * (1 / self.pixel_ratio) loc = self.position + offset return loc.update(y=-loc.y) diff --git a/ppb/engine.py b/ppb/engine.py index 4101c4a4..586922a7 100644 --- a/ppb/engine.py +++ b/ppb/engine.py @@ -1,8 +1,8 @@ +import time from collections import defaultdict from collections import deque from contextlib import ExitStack from itertools import chain -import time from typing import Any from typing import Callable from typing import DefaultDict @@ -12,9 +12,7 @@ import ppb.events as events from ppb.abc import Engine -from ppb.events import StartScene -from ppb.events import EventMixin -from ppb.events import Quit +from ppb.events import EventMixin, Quit, StartScene from ppb.systems import PygameEventPoller from ppb.systems import Renderer from ppb.systems import Updater @@ -40,7 +38,8 @@ def __init__(self, first_scene: Type, *, # Engine State self.scenes = [] self.events = deque() - self.event_extensions: DefaultDict[Union[Type, _ellipsis], List[Callable[[Any], None]]] = defaultdict(list) + self.event_extensions: DefaultDict[Union[Type, _ellipsis], + List[Callable[[Any], None]]] = defaultdict(list) self.running = False self.entered = False self._last_idle_time = None diff --git a/ppb/events.py b/ppb/events.py index e6ab74a1..84daa749 100644 --- a/ppb/events.py +++ b/ppb/events.py @@ -1,6 +1,6 @@ -from dataclasses import dataclass import logging import re +from dataclasses import dataclass from typing import Any from typing import Collection from typing import Dict @@ -8,10 +8,10 @@ from typing import Type from typing import Union +from ppb import Vector from ppb.abc import Scene from ppb.buttons import MouseButton from ppb.keycodes import KeyCode -from ppb import Vector __all__ = ( 'StartScene', @@ -31,6 +31,7 @@ boundaries_finder = re.compile('(.)([A-Z][a-z]+)') boundaries_finder_2 = re.compile('([a-z0-9])([A-Z])') + def camel_to_snake(txt): s1 = boundaries_finder.sub(r'\1_\2', txt) return boundaries_finder_2.sub(r'\1_\2', s1).lower() @@ -46,9 +47,10 @@ def __init__(self, instance, method, event): article = ['a', 'an'][int(e_name.lower()[0] in "aeiou")] message = f""" -{o_name}.{method}() signature incorrect, it should accept {article} {e_name} object and a signal function. +The signature of {o_name}.{method}() is incorrect: +it should accept {article} {e_name} object and a signal function. -{e_name} is a dataclass that represents an event. Its attributes +{e_name} is a dataclass that represents an event. Its attributes tell you about the event. The signal function is a function you can call that accepts an event instance diff --git a/ppb/features/animation.py b/ppb/features/animation.py index f4b448ef..e0e87b99 100644 --- a/ppb/features/animation.py +++ b/ppb/features/animation.py @@ -3,8 +3,8 @@ Only supports frame-by-frame, not gif, apng, or full motion video. """ -import time import re +import time FILE_PATTERN = re.compile(r'\{(\d+)\.\.(\d+)\}') diff --git a/ppb/flags.py b/ppb/flags.py index 65c97f3f..fa12676a 100644 --- a/ppb/flags.py +++ b/ppb/flags.py @@ -27,7 +27,7 @@ class FlagMeta(type): """ Metaclass for Flag. You probably want that instead. """ - def __new__(mcls, *p, abstract=False, **kw): + def __new__(mcls, *p, abstract=False, **kw): # noqa: N804 cls = super().__new__(mcls, *p, **kw) if abstract: cls._instance = ... diff --git a/ppb/keycodes.py b/ppb/keycodes.py index ab0b9ff4..56a97537 100644 --- a/ppb/keycodes.py +++ b/ppb/keycodes.py @@ -48,7 +48,7 @@ class H(KeyCode): "" -class I(KeyCode): +class I(KeyCode): # noqa: E742 "" @@ -72,7 +72,7 @@ class N(KeyCode): "" -class O(KeyCode): +class O(KeyCode): # noqa: E742 "" @@ -291,7 +291,7 @@ class AltLeft(KeyCode): class Backslash(KeyCode): """ \\ - + Shift+\\ is | on american keyboards """ @@ -303,7 +303,7 @@ class Backspace(KeyCode): class BracketLeft(KeyCode): """ [ - + Shift+[ is { on american keyboards """ @@ -311,7 +311,7 @@ class BracketLeft(KeyCode): class BracketRight(KeyCode): """ ] - + Shift+] is } on american keyboards """ @@ -323,7 +323,7 @@ class CapsLock(KeyCode): class Comma(KeyCode): """ , - + Shift+, is < on american keyboards """ @@ -395,7 +395,7 @@ class Menu(KeyCode): class Minus(KeyCode): """ - - + Shift+- is _ on american keyboards """ @@ -415,7 +415,7 @@ class PageUp(KeyCode): class Pause(KeyCode): """ Pause, generally lives next to Print Screen and Scroll Lock. - + Also Break. """ @@ -423,7 +423,7 @@ class Pause(KeyCode): class Period(KeyCode): """ . - + Shift+. is > on american keyboards """ @@ -499,4 +499,4 @@ class Up(KeyCode): # Numpad codes (unified between pygame and pyglet) # 0 1 2 3 4 5 6 7 8 9 add begin decimal delete divide down end enter equals # f1 f2 f3 f4 home insert left minus multiply next page down page up period plus -# prior right separator space subtract tab up +# prior right separator space subtract tab up diff --git a/ppb/scenes.py b/ppb/scenes.py index cb2e61d3..0545f867 100644 --- a/ppb/scenes.py +++ b/ppb/scenes.py @@ -6,9 +6,7 @@ from typing import Iterable from typing import Iterator from typing import Sequence -from typing import Tuple from typing import Type -from warnings import warn from ppb.abc import Scene from ppb.camera import Camera @@ -32,7 +30,7 @@ def __iter__(self) -> Iterator[Hashable]: def __len__(self) -> int: return len(self.all) - def add(self, game_object: Hashable, tags: Iterable[Hashable]=()) -> None: + def add(self, game_object: Hashable, tags: Iterable[Hashable] = ()) -> None: """ Add a game_object to the container. @@ -46,7 +44,10 @@ def add(self, game_object: Hashable, tags: Iterable[Hashable]=()) -> None: container.add(MyObject(), tags=("red", "blue") """ if isinstance(tags, (str, bytes)): - raise TypeError("You passed a string instead of an iterable, this probably isn't what you intended.\n\nTry making it a tuple.") + raise TypeError( + "You passed a string instead of an iterable, this probably isn't what you intended." + "\n\nTry making it a tuple.", + ) self.all.add(game_object) for kind in type(game_object).mro(): @@ -54,7 +55,7 @@ def add(self, game_object: Hashable, tags: Iterable[Hashable]=()) -> None: for tag in tags: self.tags[tag].add(game_object) - def get(self, *, kind: Type=None, tag: Hashable=None, **_) -> Iterator: + def get(self, *, kind: Type = None, tag: Hashable = None, **_) -> Iterator: """ Get an iterator of objects by kind or tag. @@ -104,8 +105,8 @@ class BaseScene(Scene, EventMixin): background_color: Sequence[int] = (0, 0, 100) container_class: Type = GameObjectCollection - def __init__(self, engine, *, - set_up: Callable=None, pixel_ratio: Number=64, + def __init__(self, engine, *, + set_up: Callable = None, pixel_ratio: Number = 64, **kwargs): super().__init__(engine) for k, v in kwargs.items(): @@ -141,7 +142,7 @@ def main_camera(self, value: Camera): self.game_objects.remove(camera) self.game_objects.add(value, tags=["main_camera"]) - def add(self, game_object: Hashable, tags: Iterable=())-> None: + def add(self, game_object: Hashable, tags: Iterable = ()) -> None: """ Add a game_object to the scene. @@ -156,7 +157,7 @@ def add(self, game_object: Hashable, tags: Iterable=())-> None: """ self.game_objects.add(game_object, tags) - def get(self, *, kind: Type=None, tag: Hashable=None, **kwargs) -> Iterator: + def get(self, *, kind: Type = None, tag: Hashable = None, **kwargs) -> Iterator: """ Get an iterator of GameObjects by kind or tag. diff --git a/ppb/sprites.py b/ppb/sprites.py index 7653ece5..168ad516 100644 --- a/ppb/sprites.py +++ b/ppb/sprites.py @@ -2,12 +2,11 @@ from pathlib import Path from typing import Union +import ppb_vector from ppb import Vector from ppb.events import EventMixin from ppb.utils import FauxFloat -import ppb_vector - TOP = "top" BOTTOM = "bottom" @@ -23,7 +22,7 @@ class Side(FauxFloat): LEFT: ('x', -1), RIGHT: ('x', 1), TOP: ('y', -1), - BOTTOM: ('y', 1) + BOTTOM: ('y', 1), } def __init__(self, parent: 'BaseSprite', side: str): @@ -133,7 +132,7 @@ def _mk_update_vector_center(self, value): fields = { self_dimension: value[self_dimension] - self_offset, - attr_dimension: value[attr_dimension] + attr_dimension: value[attr_dimension], } return Vector(fields) diff --git a/ppb/systems/__init__.py b/ppb/systems/__init__.py index 926d86e8..8aaf03f3 100644 --- a/ppb/systems/__init__.py +++ b/ppb/systems/__init__.py @@ -21,12 +21,14 @@ def __exit__(self, exc_type, exc_val, exc_tb): pass -from ppb.systems.pg import EventPoller as PygameEventPoller # To not break old imports. +# This import is here for backwards-compatibility reasons. +from ppb.systems.pg import EventPoller as PygameEventPoller # noqa: E402,F401,I202 class Renderer(System): - def __init__(self, resolution=default_resolution, window_title: str="PursuedPyBear", target_frame_rate: int=30, **kwargs): + def __init__(self, resolution=default_resolution, + window_title: str = "PursuedPyBear", target_frame_rate: int = 30, **kwargs): self.resolution = resolution self.resources = {} self.window = None @@ -121,7 +123,7 @@ def resize_image(self, image, game_unit_size): # TODO: Pygame specific code To be abstracted somehow. key = (image, game_unit_size) resized_image = self.old_resized_images.get(key) - if resized_image is None: + if resized_image is None: height = image.get_height() width = image.get_width() target_resolution = self.target_resolution(width, @@ -162,6 +164,7 @@ def on_idle(self, idle_event: events.Idle, signal): self.accumulated_time += this_tick - self.last_tick self.last_tick = this_tick while self.accumulated_time >= self.time_step: - # This might need to change for the Idle event system to signal _only_ once per idle event. + # This might need to change for the Idle event system + # to signal _only_ once per idle event. self.accumulated_time += -self.time_step signal(events.Update(self.time_step)) diff --git a/ppb/systems/pg.py b/ppb/systems/pg.py index e830613e..521928a7 100644 --- a/ppb/systems/pg.py +++ b/ppb/systems/pg.py @@ -3,11 +3,11 @@ import pygame import ppb.buttons as buttons -from ppb import Vector from ppb import events from ppb import keycodes as keys -from ppb.systems import System # TODO: Be aware of circular imports +from ppb import Vector from ppb.systems import default_resolution +from ppb.systems import System # TODO: Be aware of circular imports class EventPoller(System): @@ -130,7 +130,7 @@ class EventPoller(System): pygame.KMOD_LMETA: keys.SuperLeft, pygame.KMOD_RMETA: keys.SuperRight, pygame.KMOD_NUM: keys.NumLock, - pygame.KMOD_CAPS: keys.CapsLock + pygame.KMOD_CAPS: keys.CapsLock, } def __new__(cls, *args, **kwargs): @@ -141,7 +141,7 @@ def __new__(cls, *args, **kwargs): pygame.MOUSEBUTTONDOWN: "button_pressed", pygame.MOUSEBUTTONUP: "button_released", pygame.KEYDOWN: "key_pressed", - pygame.KEYUP: "key_released" + pygame.KEYUP: "key_released", } return super().__new__(cls) @@ -170,9 +170,9 @@ def mouse_motion(self, event, scene): screen_position = Vector(*event.pos) camera = scene.main_camera scene_position = camera.translate_to_frame(screen_position) - delta = Vector(*event.rel) * (1/camera.pixel_ratio) + delta = Vector(*event.rel) * (1 / camera.pixel_ratio) buttons = { - self.button_map[btn+1] + self.button_map[btn + 1] for btn, value in enumerate(event.buttons) if value } diff --git a/ppb/testutils.py b/ppb/testutils.py index b36daade..5a633954 100644 --- a/ppb/testutils.py +++ b/ppb/testutils.py @@ -10,7 +10,7 @@ class Failer(System): def __init__(self, *, fail: Callable[[GameEngine], bool], message: str, - run_time: float=1, engine, **kwargs): + run_time: float = 1, engine, **kwargs): super().__init__(**kwargs) self.fail = fail self.message = message diff --git a/ppb/utils.py b/ppb/utils.py index fc01952b..95e71651 100644 --- a/ppb/utils.py +++ b/ppb/utils.py @@ -1,9 +1,9 @@ import logging -import sys -import numbers import math +import numbers +import sys -__all__ = 'LoggingMixin', 'FauxFloat', +__all__ = 'LoggingMixin', 'FauxFloat' # Dictionary mapping file names -> module names diff --git a/requirements-lint.txt b/requirements-lint.txt new file mode 100644 index 00000000..74e7de44 --- /dev/null +++ b/requirements-lint.txt @@ -0,0 +1,5 @@ +flake8 +flake8-bugbear +flake8-commas +flake8-import-order +pep8-naming diff --git a/setup.cfg b/setup.cfg index e0e90bdc..83197b92 100644 --- a/setup.cfg +++ b/setup.cfg @@ -34,3 +34,12 @@ setup_requires = pytest-runner python_requires = >= 3.6 + + +[flake8] +select = C,E,F,I,W,N,B,B9 +ignore = E704,W504 +max-line-length = 100 + +application-import-names = ppb,ppb_vector +import-order-style = smarkets diff --git a/tests/test_camera.py b/tests/test_camera.py index b28bfec7..e14e29c4 100644 --- a/tests/test_camera.py +++ b/tests/test_camera.py @@ -52,7 +52,9 @@ def test_sprite_in_viewport(): cam = Camera(viewport=(0, 0, 800, 600), pixel_ratio=80) class Thing(BaseSprite): - def __init__(self, position=Vector(2, 2)): + default_pos = Vector(2, 2) + + def __init__(self, position=default_pos): super().__init__() self.size = 2 self.position = position diff --git a/tests/test_engine.py b/tests/test_engine.py index bb74a417..25f5eb4a 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -4,7 +4,7 @@ from pygame import Surface -from ppb import GameEngine, BaseScene, Vector +from ppb import BaseScene, GameEngine, Vector from ppb import events from ppb.systems import System from ppb.systems import Updater @@ -37,6 +37,7 @@ def test_signal(): engine.run() assert not engine.running + def test_signal_once(): engine = GameEngine(BaseScene, systems=[Quitter]) @@ -208,7 +209,8 @@ def on_scene_stopped(self, event, signal): assert event.scene is self test_function() - with GameEngine(TestScene, systems=[Updater, Failer], fail=lambda x: False, message="Will only time out.") as ge: + with GameEngine(TestScene, systems=[Updater, Failer], + fail=lambda x: False, message="Will only time out.") as ge: ge.run() test_function.assert_called() @@ -233,7 +235,6 @@ def test_event_extension(): class TestEvent: pass - class TestScene(BaseScene): def __init__(self, engine): @@ -250,7 +251,8 @@ def on_test_event(self, event, signal): def event_extension(self, event): event.test_value = "Red" - with GameEngine(TestScene, systems=[Updater, Failer], message="Will only time out.", fail=lambda x: False) as ge: + with GameEngine(TestScene, systems=[Updater, Failer], + message="Will only time out.", fail=lambda x: False) as ge: ge.run() @@ -263,7 +265,6 @@ def all_extension(event): class TestEvent: pass - class TestScene(BaseScene): def on_update(self, event, signal): @@ -300,5 +301,6 @@ def on_idle(self, event: events.Idle, signal): was_called = True signal(events.Quit()) - with GameEngine(BaseScene, systems=[TestSystem, Failer], fail=lambda x: False, message="Can only time out.") as ge: + with GameEngine(BaseScene, systems=[TestSystem, Failer], + fail=lambda x: False, message="Can only time out.") as ge: ge.run() diff --git a/tests/test_events.py b/tests/test_events.py index 1b73e2a6..928269e4 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -20,7 +20,7 @@ def on_spam(self, bag, fire_event): passed_bag = bag bag = Spam() - fire_event = lambda: None + def fire_event(): return None e = Eventable() @@ -34,11 +34,9 @@ def test_event_mixin_with_bad_signature(): class BadSpam: pass - class Spam: pass - class Eventable(EventMixin): def on_spam(self, spam_event): pass diff --git a/tests/test_fauxfloat.py b/tests/test_fauxfloat.py index 001bfe1a..e86f28d7 100644 --- a/tests/test_fauxfloat.py +++ b/tests/test_fauxfloat.py @@ -2,12 +2,10 @@ import operator import typing -from ppb.utils import FauxFloat - import pytest +from hypothesis import assume, given, strategies as st -from hypothesis import given, assume -import hypothesis.strategies as st +from ppb.utils import FauxFloat class RealFauxFloat(FauxFloat): @@ -21,7 +19,6 @@ def __float__(self): return self.num - # The use of pytest.mark.parametrize is prefered to st.sampled_from for # operators and datatypes for 2 reasons: # - It guarantees the parameter space is explored exhaustively. diff --git a/tests/test_flags.py b/tests/test_flags.py index d7677910..5152ad3d 100644 --- a/tests/test_flags.py +++ b/tests/test_flags.py @@ -1,5 +1,7 @@ import inspect + import pytest + from ppb.flags import Flag @@ -11,14 +13,18 @@ class TestFlag(Flag): def test_subclass(): - class SpecialFlag(Flag, abstract=True): pass - class TestFlag(SpecialFlag): pass + class SpecialFlag(Flag, abstract=True): + pass + + class TestFlag(SpecialFlag): + pass - assert isinstance(TestFlag, SpecialFlag) + assert isinstance(TestFlag, SpecialFlag) def test_instantiate(): - class TestFlag(Flag): pass + class TestFlag(Flag): + pass with pytest.raises(TypeError): TestFlag() @@ -28,18 +34,21 @@ class TestFlag(Flag): pass def test_abstract_instantiate(): - class SpecialFlag(Flag, abstract=True): pass + class SpecialFlag(Flag, abstract=True): + pass - with pytest.raises(TypeError): + with pytest.raises(TypeError): SpecialFlag() def test_comparison(): - class FlagA(Flag): pass - class FlagB(Flag): pass + class FlagA(Flag): + pass - assert FlagA is FlagA - assert FlagB is not FlagA - assert FlagA == FlagA - assert FlagB != FlagA + class FlagB(Flag): + pass + assert FlagA is FlagA + assert FlagB is not FlagA + assert FlagA == FlagA + assert FlagB != FlagA diff --git a/tests/test_scenes.py b/tests/test_scenes.py index e6454ac8..aac7008e 100644 --- a/tests/test_scenes.py +++ b/tests/test_scenes.py @@ -4,9 +4,8 @@ from pytest import mark from pytest import raises -from ppb.scenes import BaseScene from ppb.camera import Camera -from ppb.scenes import GameObjectCollection +from ppb.scenes import BaseScene, GameObjectCollection class TestEnemy: diff --git a/tests/test_testutil.py b/tests/test_testutil.py index f2e5f4bf..b2375e54 100644 --- a/tests/test_testutil.py +++ b/tests/test_testutil.py @@ -13,7 +13,7 @@ def test_quitter(loop_count): quitter = testutil.Quitter(loop_count=loop_count) signal_mock = Mock() - for i in range(loop_count): + for _ in range(loop_count): quitter.__event__(Idle(.01), signal_mock) signal_mock.assert_called_once() assert len(signal_mock.call_args[0]) == 1 @@ -29,7 +29,8 @@ def test_failer_immediate(): def test_failer_timed(): - failer = testutil.Failer(fail=lambda e: False, message="Should time out", run_time=0.1, engine=None) + failer = testutil.Failer(fail=lambda e: False, message="Should time out", + run_time=0.1, engine=None) start_time = monotonic()