diff --git a/.github/workflows/test-suite.yml b/.github/workflows/test-suite.yml index 787dbfe..5b936ab 100644 --- a/.github/workflows/test-suite.yml +++ b/.github/workflows/test-suite.yml @@ -20,7 +20,7 @@ jobs: runs-on: "ubuntu-latest" strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] services: postgres: diff --git a/lilya/middleware/compression.py b/lilya/middleware/compression.py index e4f03d0..dfcec08 100644 --- a/lilya/middleware/compression.py +++ b/lilya/middleware/compression.py @@ -2,6 +2,7 @@ import gzip import io +from functools import cached_property from typing import NoReturn from lilya.datastructures import Header @@ -79,9 +80,10 @@ def __init__(self, app: ASGIApp, minimum_size: int, compresslevel: int = 9) -> N self.started = False self.content_encoding_set = False self.gzip_buffer = io.BytesIO() - self.gzip_file = gzip.GzipFile( - mode="wb", fileobj=self.gzip_buffer, compresslevel=self.compresslevel - ) + + @cached_property + def gzip_file(self) -> gzip.GzipFile: + return gzip.GzipFile(mode="wb", fileobj=self.gzip_buffer, compresslevel=self.compresslevel) async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: """ @@ -93,7 +95,12 @@ async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None: send (Send): The send channel. """ self.send = send - await self.app(scope, receive, self.send_with_gzip) + try: + await self.app(scope, receive, self.send_with_gzip) + finally: + # ensure cleanup if file is initialized + if "gzip_file" in self.__dict__ and not self.gzip_file.closed: + self.gzip_file.close() async def send_with_gzip(self, message: Message) -> None: """ diff --git a/lilya/middleware/server_error.py b/lilya/middleware/server_error.py index 609c055..e8db497 100644 --- a/lilya/middleware/server_error.py +++ b/lilya/middleware/server_error.py @@ -195,10 +195,12 @@ def generate_html(self, exc: Exception) -> str: is_collapsed = True # escape error class and text - error = ( - f"{html.escape(traceback_obj.exc_type.__name__)}: " - f"{html.escape(str(traceback_obj))}" - ) + try: + exc_type_str = traceback_obj.exc_type_str + except Exception: + # for older python versions < 3.13 + exc_type_str = traceback_obj.exc_type.__name__ + error = f"{html.escape(exc_type_str)}: " f"{html.escape(str(traceback_obj))}" template = get_template_errors() return template.format(styles=get_css_style(), js=get_js(), error=error, exc_html=exc_html)