Skip to content

Commit

Permalink
Merge pull request #4 from Dooders:logging
Browse files Browse the repository at this point in the history
Logging
  • Loading branch information
csmangum authored Aug 23, 2024
2 parents 5595478 + 3979adb commit 7a988fc
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 0 deletions.
112 changes: 112 additions & 0 deletions fizicks/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
"""
This logging solution uses a decorator combined with a `LogConfig` object to
provide customized logging for methods in a Python class. Here's how it works:
1. **LogConfig Object**: A `LogConfig` class encapsulates logging instructions,
including custom messages (`before_msg`, `after_msg`) and additional data
(`before_data`, `after_data`) to be logged before and after a method is executed.
2. **Decorator**: The `log_event` decorator accepts a `CollisionLogger`
instance and a `LogConfig` object. It wraps the target method, executing
the logging instructions specified in the `LogConfig` before and after the
method runs.
3. **Method Decoration**: Methods are decorated with `@log_event`, passing the
appropriate `LogConfig` object. This setup automatically handles logging
without cluttering the method logic.
4. **Custom Logging**: When a method is executed, if logging is enabled
(`debug=True`), the decorator logs customized messages and data before and
after the method’s core logic, based on the configurations provided in the
`LogConfig`.
"""

import logging
from functools import wraps
from typing import Callable

logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)

# Create a console handler and set the level
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# Create a formatter and set it for the handler
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
console_handler.setFormatter(formatter)

# Add the handler to the logger
logger.addHandler(console_handler)


class LogConfig:
"""
Configuration for logging events.
"""

def __init__(
self,
before_msg: str = None,
after_msg: str = None,
before_data: dict = None,
after_data: dict = None,
):
self.before_msg = before_msg
self.after_msg = after_msg
self.before_data = before_data or {}
self.after_data = after_data or {}


def log_event(config: LogConfig, debug: bool = True) -> Callable:
"""
Decorator to log events with the given config.
Parameters
----------
config : LogConfig
The configuration for the logging event.
debug : bool, optional
Whether to log the event in debug mode, by default False.
"""

def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
debug = kwargs.get("debug", True)
obj = args[1] if len(args) > 1 else None
obj_desc = obj.description() if obj else ""
method_name = func.__name__

if debug:
if config.before_msg:
custom_before_msg = config.before_msg.format(
method_name=method_name, object_desc=obj_desc
)
logger.debug(
custom_before_msg,
{**config.before_data, "Args": args, "Kwargs": kwargs},
)

result = func(*args, **kwargs)

if debug:
if config.after_msg:
custom_after_msg = config.after_msg.format(
method_name=method_name, object_desc=obj_desc
)
logger.debug(
custom_after_msg,
{
**config.after_data,
"Result": result,
"Args": args,
"Kwargs": kwargs,
},
)

return result

return wrapper

return decorator
92 changes: 92 additions & 0 deletions fizicks/visual.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from typing import Any, List

import pygame

from fizicks.collision import Collision


class VisualDebugger:
"""
A class for visualizing the universe and objects for debugging purposes.
Uses Pygame to visualize the universe and objects.
Parameters
----------
universe: Universe
The universe to visualize.
objects: List[Matter]
The objects to visualize.
"""

def __init__(self, universe: "Universe", objects: List["Matter"]):
self.universe = universe
self.objects = objects

# Pygame initialization
pygame.init()
self.screen = pygame.display.set_mode(
(int(universe.dimensions.x), int(universe.dimensions.y))
)
pygame.display.set_caption("Fizicks Visual Debugger")
self.clock = pygame.time.Clock()

def draw_object(self, obj: "Matter") -> None:
"""
Draws an object on the screen.
Parameters
----------
obj: Matter
The object to draw.
"""
pygame.draw.circle(
self.screen,
obj.color,
(int(obj.position.x), int(obj.position.y)),
int(obj.radius),
)

def draw_border(self) -> None:
"""
Draws the border of the universe on the screen.
"""
pygame.draw.rect(
self.screen,
(255, 255, 255),
pygame.Rect(
0, 0, int(self.universe.dimensions.x), int(self.universe.dimensions.y)
),
1,
)

def run(self) -> None:
"""
Runs the simulation and visualizes the universe and objects.
"""
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False

self.screen.fill((0, 0, 0)) # Clear screen with black

self.draw_border()

# Identify collisions
for i, obj in enumerate(self.objects):
for other_obj in self.objects[i + 1 :]:
if obj.collides_with(other_obj):
Collision.resolve(obj, other_obj)

for obj in self.objects:
obj.update(self.universe)
self.draw_object(obj)

self.universe.time += 1

pygame.display.flip()
self.clock.tick(60) # Cap the frame rate at 60 FPS

pygame.quit()

0 comments on commit 7a988fc

Please sign in to comment.