Skip to content

Commit

Permalink
chore: capture return values from a decorated function
Browse files Browse the repository at this point in the history
In order to minimize impact and do not change behaviour of
existing code, this will provide a decorator to capture
return values of any function (that returns a json serializable
object) and be able to store/dump that value in a .json file for
further inspection.

Refs #4489
  • Loading branch information
TeeeJay committed May 24, 2023
1 parent 064adfa commit 09ef23e
Showing 1 changed file with 80 additions and 0 deletions.
80 changes: 80 additions & 0 deletions src/ecalc/libraries/libecalc/common/libecalc/common/capturer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import os
import typing
from functools import wraps
from pathlib import Path
from typing import Any, Callable, Optional

from libecalc.common.logger import logger

unique_id: int = 1
"""
global static id to increase by one for each file being dumped
"""


class Jsonable(typing.Protocol):
"""
Protocol to make sure that functions that are decorated for capture
can be serialized to json through the json() method
"""

def json(self) -> str:
"""
Serialize object as JSON
:return: a string according to json spec
"""
...


def save(output_directory: Path, content: Jsonable):
"""
A function to save serializable content. Each time this function is
called the name of the file that data is dumped to (which is an integer) will be incremented with 1.
E.g. file number one will be given the name 1.json, number two; 2.json and so on.
:param output_directory: Where to save it. Missing parent directories will be created.
:param content: What to save. Must be serializable to JSON through .json() method
"""
Path.mkdir(output_directory, parents=True, exist_ok=True)
global unique_id
file_name = output_directory / f"{str(unique_id)}.json"
unique_id += 1

with open(file_name, "w") as file:
file.write(content.json())


class Capturer:
"""Decorator to capture values from a decorated function and optionally save to file"""

@staticmethod
def capture_return_values(
do_save_captured_content: bool = False, output_directory: Path = Path(os.getcwd()) / "captured_data"
) -> Optional[Any]:
"""If enabled, save the return values from decorated function to the given output directory. If disabled,
the function works as if the decorator is not there.
Note! This currently only supports functions that returns a value/object that support serialization to json() through implementing a .json() method
Args:
do_save_captured_content: whether the captured content should be saved or not. Default False.
output_directory: Directory to where the captured content should be stored. Missing parent directories will be created.
Returns:
The same signature as the function it is decorating
"""

def decorate(capturable: Callable[..., Jsonable]):
@wraps(capturable)
def with_capture(self, *args, **kwargs):
return_values = capturable(self, *args, **kwargs)
try:
save(output_directory, return_values) if do_save_captured_content is True else ...
except Exception as e:
logger.debug(f"Failed to dump data for debug {str(e)}. Not critical.")
return return_values

return with_capture

return decorate

0 comments on commit 09ef23e

Please sign in to comment.