clan_vm_manager: Add profiler component
All checks were successful
checks / check-links (pull_request) Successful in 21s
checks / checks-impure (pull_request) Successful in 1m57s
checks / checks (pull_request) Successful in 2m53s

This commit is contained in:
Luis Hebendanz 2024-03-04 14:01:30 +07:00
parent 31190ed8e5
commit f70879aa63
2 changed files with 73 additions and 4 deletions

View File

@ -1,13 +1,13 @@
import logging
import sys
from .app import MainApplication
from clan_vm_manager.app import MainApplication
from clan_vm_manager.components.profiler import profile
log = logging.getLogger(__name__)
# TODO: Trayicon support
# https://github.com/nicotine-plus/nicotine-plus/blob/b08552584eb6f35782ad77da93ae4aae3362bf64/pynicotine/gtkgui/widgets/trayicon.py#L982
def main() -> None:
@profile
def main() -> int:
app = MainApplication()
return app.run(sys.argv)

View File

@ -0,0 +1,69 @@
import cProfile
import io
import logging
import os
import pstats
import re
from collections.abc import Callable
from typing import Any
# Ensure you have a logger set up for logging exceptions
log = logging.getLogger(__name__)
def trim_path_to_three_levels(path: str) -> str:
parts = path.split(os.path.sep)
if len(parts) > 4:
return os.path.sep.join(parts[-4:])
else:
return path
def profile(func: Callable) -> Callable:
"""
A decorator that profiles the decorated function, printing out the profiling
results with paths trimmed to three directories deep.
"""
def wrapper(*args: Any, **kwargs: Any) -> Any:
pr = cProfile.Profile()
res: Any = None
try:
pr.enable()
res = func(*args, **kwargs)
pr.disable()
except Exception as ex:
log.exception(ex)
pr.disable()
return None
s = io.StringIO()
ps = pstats.Stats(pr, stream=s)
ps.sort_stats(pstats.SortKey.CUMULATIVE)
ps.print_stats(12)
# Process the output to trim file paths
output_lines = s.getvalue().split("\n")
for line in output_lines:
try:
parts = re.split(r"\s+", line)[
1:
] # Split on the first space to separate the time from the path
fqpath = parts[-1]
fpath, line_num = fqpath.split(":")
if os.path.sep in fpath: # Check if this looks like it includes a path
fpath = trim_path_to_three_levels(fpath)
prefix = f"{parts[0]:>7}"
prefix += f"{parts[1]:>9}"
prefix += f"{parts[2]:>9}"
prefix += f"{parts[3]:>9}"
prefix += f"{parts[4]:>9}"
new_line = f"{prefix:} {fpath}:{line_num}"
else:
new_line = line
except (ValueError, IndexError):
new_line = line # If there's no path, leave the line as is
print(new_line)
return res
return wrapper