1 Commits

Author SHA1 Message Date
David Rodenkirchen 68c51a09f8 Analyze mem leak 2026-04-15 09:29:56 +02:00
8 changed files with 606 additions and 923 deletions
BIN
View File
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+46
View File
@@ -1,9 +1,15 @@
import logging import logging
import tracemalloc
import sys import sys
from pathlib import Path from pathlib import Path
from uuid import uuid4 from uuid import uuid4
import gc
import time
import threading
from collections import Counter
from datetime import datetime, UTC
from rio import App, Theme, Color, Font, ComponentPage, Session from rio import App, Theme, Color, Font, ComponentPage, Session
from from_root import from_root from from_root import from_root
@@ -13,9 +19,49 @@ from src.ezgg_lan_manager.helpers.LoggedInGuard import logged_in_guard, not_logg
from src.ezgg_lan_manager.services.LocalDataService import LocalData from src.ezgg_lan_manager.services.LocalDataService import LocalData
from src.ezgg_lan_manager.types.UserSession import UserSession from src.ezgg_lan_manager.types.UserSession import UserSession
tracemalloc.start(25)
logger = logging.getLogger("EzggLanManager") logger = logging.getLogger("EzggLanManager")
def log_object_summary():
gc.collect()
objs = gc.get_objects()
counter = Counter(type(obj).__name__ for obj in objs)
timestamp = datetime.now(UTC).isoformat()
with open("memory_objects.log", "a") as f:
f.write(f"\n=== {timestamp} ===\n")
f.write(f"Total objects: {len(objs)}\n")
for name, count in counter.most_common(25):
f.write(f"{name}: {count}\n")
def log_top_allocations():
snapshot = tracemalloc.take_snapshot()
top_stats = snapshot.statistics('lineno')
timestamp = datetime.now(UTC).isoformat()
with open("memory_allocations.log", "a") as f:
f.write(f"\n=== {timestamp} ===\n")
for stat in top_stats[:10]:
f.write(str(stat) + "\n")
def start_hourly_logger():
def loop():
while True:
log_object_summary()
log_top_allocations()
time.sleep(3) # 1 hour
t = threading.Thread(target=loop, daemon=True)
t.start()
if __name__ == "__main__": if __name__ == "__main__":
start_hourly_logger()
theme = Theme.from_colors( theme = Theme.from_colors(
primary_color=Color.from_hex("ffffff"), primary_color=Color.from_hex("ffffff"),
secondary_color=Color.from_hex("018786"), secondary_color=Color.from_hex("018786"),
@@ -60,10 +60,8 @@ class UserInfoBox(Component):
self.session[LocalDataService].del_session(self.session[LocalData].stored_session_token) self.session[LocalDataService].del_session(self.session[LocalData].stored_session_token)
self.session[LocalData].stored_session_token = None self.session[LocalData].stored_session_token = None
self.session.attach(self.session[LocalData]) self.session.attach(self.session[LocalData])
if self.status_change_cb is not None:
await self.status_change_cb() await self.status_change_cb()
await self.session[RefreshService].trigger_refresh() await self.session[RefreshService].trigger_refresh()
self.session.navigate_to("")
@event.on_populate @event.on_populate
async def async_init(self) -> None: async def async_init(self) -> None:
+2 -12
View File
@@ -1,7 +1,7 @@
from decimal import Decimal from decimal import Decimal
from typing import Optional from typing import Optional
from rio import Column, Component, event, Text, TextStyle, Button, Color, Revealer, Row, ProgressCircle, Link, Image from rio import Column, Component, event, Text, TextStyle, Button, Color, Revealer, Row, ProgressCircle, Link
from src.ezgg_lan_manager import ConfigurationService, UserService, AccountingService from src.ezgg_lan_manager import ConfigurationService, UserService, AccountingService
from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox from src.ezgg_lan_manager.components.MainViewContentBox import MainViewContentBox
@@ -14,7 +14,6 @@ class AccountPage(Component):
user: Optional[User] = None user: Optional[User] = None
balance: Optional[Decimal] = None balance: Optional[Decimal] = None
transaction_history: list[Transaction] = list() transaction_history: list[Transaction] = list()
payment_qr_image: bytes = None
banking_info_revealer_open: bool = False banking_info_revealer_open: bool = False
paypal_info_revealer_open: bool = False paypal_info_revealer_open: bool = False
@@ -29,11 +28,6 @@ class AccountPage(Component):
self.user = await self.session[UserService].get_user(user_id) self.user = await self.session[UserService].get_user(user_id)
self.balance = await self.session[AccountingService].get_balance(user_id) self.balance = await self.session[AccountingService].get_balance(user_id)
self.transaction_history = await self.session[AccountingService].get_transaction_history(user_id) self.transaction_history = await self.session[AccountingService].get_transaction_history(user_id)
self.payment_qr_image = self.session[AccountingService].make_payment_qr_image(
"Einfach Zocken Gaming Gesellschaft",
"GENODE51BIK",
"DE47517624340019856607",
f"AUFLADUNG - {self.user.user_id} - {self.user.user_name}")
async def _on_banking_info_press(self) -> None: async def _on_banking_info_press(self) -> None:
self.banking_info_revealer_open = not self.banking_info_revealer_open self.banking_info_revealer_open = not self.banking_info_revealer_open
@@ -42,7 +36,7 @@ class AccountPage(Component):
self.paypal_info_revealer_open = not self.paypal_info_revealer_open self.paypal_info_revealer_open = not self.paypal_info_revealer_open
def build(self) -> Component: def build(self) -> Component:
if not self.user or not self.payment_qr_image: if not self.user and not self.balance:
return Column( return Column(
MainViewContentBox( MainViewContentBox(
ProgressCircle( ProgressCircle(
@@ -91,10 +85,6 @@ class AccountPage(Component):
margin=0, margin=0,
margin_bottom=1, margin_bottom=1,
align_x=0.5 align_x=0.5
),
Image(self.payment_qr_image,
min_width=20,
min_height=20
) )
), ),
margin=2, margin=2,
@@ -1,6 +1,4 @@
import io
import logging import logging
import qrcode
from collections.abc import Callable from collections.abc import Callable
from datetime import datetime from datetime import datetime
from decimal import Decimal, ROUND_DOWN from decimal import Decimal, ROUND_DOWN
@@ -76,29 +74,3 @@ class AccountingService:
return "0.00 €" return "0.00 €"
rounded_decimal = str(euros.quantize(Decimal(".01"), rounding=ROUND_DOWN)) rounded_decimal = str(euros.quantize(Decimal(".01"), rounding=ROUND_DOWN))
return f"{rounded_decimal}" return f"{rounded_decimal}"
@staticmethod
def make_payment_qr_image(beneficiary_name, beneficiary_bic, beneficiary_iban, text, amount_euros=None) -> bytes:
text = text.replace("\n",";")
amount_formatted = "EUR{:.2f}".format(amount_euros) if amount_euros else ""
epc_text = f"""BCD
002
1
SCT
{beneficiary_bic}
{beneficiary_name}
{beneficiary_iban}
{amount_formatted}
{text}
"""
qr = qrcode.QRCode(
version=6,
error_correction=qrcode.constants.ERROR_CORRECT_M,
)
qr.add_data(epc_text)
img = qr.make_image()
img_bytes = io.BytesIO()
img.save(img_bytes)
return img_bytes.getvalue()
@@ -21,6 +21,5 @@ class LocalDataService:
self._session[key] = session self._session[key] = session
return key return key
def del_session(self, token: Optional[str]) -> None: def del_session(self, token: str) -> None:
if token is not None:
self._session.pop(token, None) self._session.pop(token, None)