-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from Dooders:logging
Logging
- Loading branch information
Showing
2 changed files
with
204 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |