From bcbd4330bb391c9bd41e42fc5c463d5f28f2cd11 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:16:45 +0000 Subject: [PATCH 01/12] Initial plan From 877291ff0bcc1402d895187c3d6003318a292a60 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:20:28 +0000 Subject: [PATCH 02/12] Initial exploration and issue reproduction Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- test_buffer_closed.py | 59 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 test_buffer_closed.py diff --git a/test_buffer_closed.py b/test_buffer_closed.py new file mode 100644 index 0000000..532996e --- /dev/null +++ b/test_buffer_closed.py @@ -0,0 +1,59 @@ +"""Test to reproduce the buffer closed issue""" +from poorwsgi.response import Response + + +def start_response(status_code, headers): + """Mock start_response function""" + print(f"Status: {status_code}") + print(f"Headers: {headers}") + return lambda data: None + + +def test_buffer_closed_after_iteration(): + """Test that buffer gets closed after WSGI server iterates over it""" + res = Response("Hello World") + + # Simulate what WSGI server does + result = res(start_response) + + # Iterate over the result (this is what WSGI server does) + for chunk in result: + print(f"Chunk: {chunk}") + + # Close the result (WSGI server closes it after iteration) + if hasattr(result, 'close'): + result.close() + + # Now try to access the data property - this should fail + try: + print(f"Data: {res.data}") + print("ERROR: Should have raised ValueError!") + except ValueError as e: + print(f"SUCCESS: Got expected error: {e}") + + +def test_buffer_seek_in_end_of_response(): + """Test that seeks on closed buffer fail""" + res = Response("Hello World") + + # Call response once + result = res(start_response) + + # Close the buffer manually (simulating what WSGI server does) + if hasattr(result, 'close'): + result.close() + + # Try to call __end_of_response__ again or access data + # This simulates the error in the stack trace + try: + data = res.data # This calls seek(0) + print(f"ERROR: Should have raised ValueError! Got: {data}") + except ValueError as e: + print(f"SUCCESS: Got expected error: {e}") + + +if __name__ == "__main__": + print("Test 1: Buffer closed after iteration") + test_buffer_closed_after_iteration() + print("\nTest 2: Buffer seek in end_of_response") + test_buffer_seek_in_end_of_response() From a9a6b1b31dadba1db0eb3ea4e23b7e12435228b0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:23:37 +0000 Subject: [PATCH 03/12] Fix I/O operation on closed file error in Response classes Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- poorwsgi/response.py | 11 +++++- test_buffer_closed.py | 85 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 82 insertions(+), 14 deletions(-) diff --git a/poorwsgi/response.py b/poorwsgi/response.py index 9836d7e..a9def76 100644 --- a/poorwsgi/response.py +++ b/poorwsgi/response.py @@ -356,6 +356,8 @@ def __init__(self, data: Union[str, bytes] = b'', @property def data(self): + if self.__buffer.closed: + return b'' self.__buffer.seek(0) return self.__buffer.read() @@ -367,10 +369,13 @@ def write(self, data: Union[str, bytes]): self.__buffer.write(data) def __end_of_response__(self): + if self.__buffer.closed: + return IBytesIO(b'') self.__buffer.seek(self._start) if self._end: return IBytesIO(self.__buffer.read(self._end - self._start + 1)) - return self.__buffer + # Return a new IBytesIO to prevent the original buffer from being closed + return IBytesIO(self.__buffer.read()) class PartialResponse(Response): @@ -502,6 +507,8 @@ def data(self): This property works only if file_obj is seekable. """ + if self.__file.closed: + return b'' if self.__file.seekable(): self.__file.seek(self.__pos) return self.__file.read() @@ -515,6 +522,8 @@ def __end_of_response__(self): This method was called from Application object at the end of request for returning right value to wsgi server. """ + if self.__file.closed: + return IBytesIO(b'') if self.__file.seekable(): self.__file.seek(self._start) if self._end: diff --git a/test_buffer_closed.py b/test_buffer_closed.py index 532996e..c0d2cf6 100644 --- a/test_buffer_closed.py +++ b/test_buffer_closed.py @@ -1,5 +1,6 @@ -"""Test to reproduce the buffer closed issue""" -from poorwsgi.response import Response +"""Test to reproduce and verify fix for the buffer closed issue""" +from io import BytesIO +from poorwsgi.response import Response, FileObjResponse def start_response(status_code, headers): @@ -10,30 +11,34 @@ def start_response(status_code, headers): def test_buffer_closed_after_iteration(): - """Test that buffer gets closed after WSGI server iterates over it""" + """Test that buffer access still works after WSGI server closes it""" res = Response("Hello World") # Simulate what WSGI server does result = res(start_response) # Iterate over the result (this is what WSGI server does) + chunks = [] for chunk in result: + chunks.append(chunk) print(f"Chunk: {chunk}") # Close the result (WSGI server closes it after iteration) if hasattr(result, 'close'): result.close() - # Now try to access the data property - this should fail + # Now try to access the data property - this should work with the fix try: - print(f"Data: {res.data}") - print("ERROR: Should have raised ValueError!") + data = res.data + print(f"SUCCESS: Can access data after buffer closed: {data}") + assert data == b"Hello World", f"Expected b'Hello World', got {data}" except ValueError as e: - print(f"SUCCESS: Got expected error: {e}") + print(f"ERROR: Got unexpected error: {e}") + raise def test_buffer_seek_in_end_of_response(): - """Test that seeks on closed buffer fail""" + """Test that seeks on closed buffer work with the fix""" res = Response("Hello World") # Call response once @@ -43,13 +48,60 @@ def test_buffer_seek_in_end_of_response(): if hasattr(result, 'close'): result.close() - # Try to call __end_of_response__ again or access data - # This simulates the error in the stack trace + # Try to access data - should work with the fix try: - data = res.data # This calls seek(0) - print(f"ERROR: Should have raised ValueError! Got: {data}") + data = res.data + print(f"SUCCESS: Can access data after buffer closed: {data}") + assert data == b"Hello World", f"Expected b'Hello World', got {data}" except ValueError as e: - print(f"SUCCESS: Got expected error: {e}") + print(f"ERROR: Got unexpected error: {e}") + raise + + +def test_multiple_calls_prevented(): + """Test that response can still only be called once""" + res = Response("Hello World") + + # First call should work + result1 = res(start_response) + chunks = list(result1) + print(f"First call succeeded, got {len(chunks)} chunk(s)") + + # Second call should raise RuntimeError + try: + result2 = res(start_response) + print("ERROR: Second call should have raised RuntimeError!") + except RuntimeError as e: + print(f"SUCCESS: Second call correctly raised RuntimeError: {e}") + + +def test_file_obj_response_closed(): + """Test that FileObjResponse handles closed files gracefully""" + file_obj = BytesIO(b"File content") + res = FileObjResponse(file_obj) + + # Simulate what WSGI server does + result = res(start_response) + + # Iterate over the result + chunks = [] + for chunk in result: + chunks.append(chunk) + print(f"File chunk: {chunk}") + + # Close the result (WSGI server closes it after iteration) + if hasattr(result, 'close'): + result.close() + + # Now try to access the data property - this should work with the fix + try: + data = res.data + print(f"SUCCESS: Can access file data after buffer closed: {data}") + # Note: data may be empty if file is closed and not seekable + assert isinstance(data, bytes), f"Expected bytes, got {type(data)}" + except ValueError as e: + print(f"ERROR: Got unexpected error: {e}") + raise if __name__ == "__main__": @@ -57,3 +109,10 @@ def test_buffer_seek_in_end_of_response(): test_buffer_closed_after_iteration() print("\nTest 2: Buffer seek in end_of_response") test_buffer_seek_in_end_of_response() + print("\nTest 3: Multiple calls prevented") + test_multiple_calls_prevented() + print("\nTest 4: FileObjResponse closed") + test_file_obj_response_closed() + print("\nAll tests passed!") + + From c86d70187be9ab7951f819a440bed3019c6f5fe4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:25:07 +0000 Subject: [PATCH 04/12] Remove temporary test file Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- test_buffer_closed.py | 118 ------------------------------------------ 1 file changed, 118 deletions(-) delete mode 100644 test_buffer_closed.py diff --git a/test_buffer_closed.py b/test_buffer_closed.py deleted file mode 100644 index c0d2cf6..0000000 --- a/test_buffer_closed.py +++ /dev/null @@ -1,118 +0,0 @@ -"""Test to reproduce and verify fix for the buffer closed issue""" -from io import BytesIO -from poorwsgi.response import Response, FileObjResponse - - -def start_response(status_code, headers): - """Mock start_response function""" - print(f"Status: {status_code}") - print(f"Headers: {headers}") - return lambda data: None - - -def test_buffer_closed_after_iteration(): - """Test that buffer access still works after WSGI server closes it""" - res = Response("Hello World") - - # Simulate what WSGI server does - result = res(start_response) - - # Iterate over the result (this is what WSGI server does) - chunks = [] - for chunk in result: - chunks.append(chunk) - print(f"Chunk: {chunk}") - - # Close the result (WSGI server closes it after iteration) - if hasattr(result, 'close'): - result.close() - - # Now try to access the data property - this should work with the fix - try: - data = res.data - print(f"SUCCESS: Can access data after buffer closed: {data}") - assert data == b"Hello World", f"Expected b'Hello World', got {data}" - except ValueError as e: - print(f"ERROR: Got unexpected error: {e}") - raise - - -def test_buffer_seek_in_end_of_response(): - """Test that seeks on closed buffer work with the fix""" - res = Response("Hello World") - - # Call response once - result = res(start_response) - - # Close the buffer manually (simulating what WSGI server does) - if hasattr(result, 'close'): - result.close() - - # Try to access data - should work with the fix - try: - data = res.data - print(f"SUCCESS: Can access data after buffer closed: {data}") - assert data == b"Hello World", f"Expected b'Hello World', got {data}" - except ValueError as e: - print(f"ERROR: Got unexpected error: {e}") - raise - - -def test_multiple_calls_prevented(): - """Test that response can still only be called once""" - res = Response("Hello World") - - # First call should work - result1 = res(start_response) - chunks = list(result1) - print(f"First call succeeded, got {len(chunks)} chunk(s)") - - # Second call should raise RuntimeError - try: - result2 = res(start_response) - print("ERROR: Second call should have raised RuntimeError!") - except RuntimeError as e: - print(f"SUCCESS: Second call correctly raised RuntimeError: {e}") - - -def test_file_obj_response_closed(): - """Test that FileObjResponse handles closed files gracefully""" - file_obj = BytesIO(b"File content") - res = FileObjResponse(file_obj) - - # Simulate what WSGI server does - result = res(start_response) - - # Iterate over the result - chunks = [] - for chunk in result: - chunks.append(chunk) - print(f"File chunk: {chunk}") - - # Close the result (WSGI server closes it after iteration) - if hasattr(result, 'close'): - result.close() - - # Now try to access the data property - this should work with the fix - try: - data = res.data - print(f"SUCCESS: Can access file data after buffer closed: {data}") - # Note: data may be empty if file is closed and not seekable - assert isinstance(data, bytes), f"Expected bytes, got {type(data)}" - except ValueError as e: - print(f"ERROR: Got unexpected error: {e}") - raise - - -if __name__ == "__main__": - print("Test 1: Buffer closed after iteration") - test_buffer_closed_after_iteration() - print("\nTest 2: Buffer seek in end_of_response") - test_buffer_seek_in_end_of_response() - print("\nTest 3: Multiple calls prevented") - test_multiple_calls_prevented() - print("\nTest 4: FileObjResponse closed") - test_file_obj_response_closed() - print("\nAll tests passed!") - - From b82fcd9ce452ccf5bba3c091b216b651309d6729 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:27:42 +0000 Subject: [PATCH 05/12] Address code review feedback - maintain sendfile optimization Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- poorwsgi/response.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/poorwsgi/response.py b/poorwsgi/response.py index a9def76..294f348 100644 --- a/poorwsgi/response.py +++ b/poorwsgi/response.py @@ -528,6 +528,8 @@ def __end_of_response__(self): self.__file.seek(self._start) if self._end: return IBytesIO(self.__file.read(self._end - self._start + 1)) + # Return the file directly to allow WSGI server to use sendfile optimization + # Note: The file will be closed by the WSGI server after iteration return self.__file From 3b4c34ab3bb55d5c4a0fd8fd7c85f962a67ddc17 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 7 Jan 2026 19:29:20 +0000 Subject: [PATCH 06/12] Add clarifying comments for code review feedback Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- poorwsgi/response.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/poorwsgi/response.py b/poorwsgi/response.py index 294f348..aee85e4 100644 --- a/poorwsgi/response.py +++ b/poorwsgi/response.py @@ -375,6 +375,7 @@ def __end_of_response__(self): if self._end: return IBytesIO(self.__buffer.read(self._end - self._start + 1)) # Return a new IBytesIO to prevent the original buffer from being closed + # After seeking to self._start, read() reads from current position to EOF return IBytesIO(self.__buffer.read()) @@ -529,7 +530,7 @@ def __end_of_response__(self): if self._end: return IBytesIO(self.__file.read(self._end - self._start + 1)) # Return the file directly to allow WSGI server to use sendfile optimization - # Note: The file will be closed by the WSGI server after iteration + # Note: When returning the file directly, it will be closed by the WSGI server return self.__file From eb2e3f4cb254fa4a236137244ae1c4fa91df4108 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 08:30:06 +0000 Subject: [PATCH 07/12] Add logging for closed buffer access and integrity tests - Add warning logs when data property is accessed on closed buffer/file - Add error logs when __end_of_response__ is called with closed buffer/file - Remove suboptimal buffer copying in Response.__end_of_response__ - Add integrity test for response closed buffer handling - Add example demonstrating response behavior Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- examples/response_closed.py | 34 +++++++++++++++++++++++++ poorwsgi/response.py | 14 ++++++---- tests_integrity/test_response_closed.py | 34 +++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 5 deletions(-) create mode 100644 examples/response_closed.py create mode 100644 tests_integrity/test_response_closed.py diff --git a/examples/response_closed.py b/examples/response_closed.py new file mode 100644 index 0000000..5121acf --- /dev/null +++ b/examples/response_closed.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python3 +"""Example demonstrating Response with closed buffer scenario.""" + +from poorwsgi import Application +from poorwsgi.response import Response + +app = Application("response_closed") + + +@app.route("/test") +def test_handler(req): + """Simple test handler that returns a Response.""" + res = Response("Test data") + # After response is sent, the buffer will be closed by WSGI server + # Accessing res.data after that should be handled gracefully + return res + + +@app.route("/test-after-response") +def test_after_response(req): + """Handler that tries to access response data after creation.""" + res = Response("Test data") + # Try to access data property immediately (should work) + data = res.data + # Just verify it works, don't log + assert data == b"Test data" + return res + + +if __name__ == "__main__": + from wsgiref.simple_server import make_server + print("Starting server on http://localhost:8080") + httpd = make_server("localhost", 8080, app) + httpd.serve_forever() diff --git a/poorwsgi/response.py b/poorwsgi/response.py index aee85e4..532793e 100644 --- a/poorwsgi/response.py +++ b/poorwsgi/response.py @@ -357,6 +357,8 @@ def __init__(self, data: Union[str, bytes] = b'', @property def data(self): if self.__buffer.closed: + log.warning("Attempt to access data from closed buffer. " + "Response has likely been sent already.") return b'' self.__buffer.seek(0) return self.__buffer.read() @@ -370,13 +372,13 @@ def write(self, data: Union[str, bytes]): def __end_of_response__(self): if self.__buffer.closed: + log.error("Buffer is closed in __end_of_response__. " + "This should not happen.") return IBytesIO(b'') self.__buffer.seek(self._start) if self._end: return IBytesIO(self.__buffer.read(self._end - self._start + 1)) - # Return a new IBytesIO to prevent the original buffer from being closed - # After seeking to self._start, read() reads from current position to EOF - return IBytesIO(self.__buffer.read()) + return self.__buffer class PartialResponse(Response): @@ -509,6 +511,8 @@ def data(self): This property works only if file_obj is seekable. """ if self.__file.closed: + log.warning("Attempt to access data from closed file. " + "Response has likely been sent already.") return b'' if self.__file.seekable(): self.__file.seek(self.__pos) @@ -524,13 +528,13 @@ def __end_of_response__(self): for returning right value to wsgi server. """ if self.__file.closed: + log.error("File is closed in __end_of_response__. " + "This should not happen.") return IBytesIO(b'') if self.__file.seekable(): self.__file.seek(self._start) if self._end: return IBytesIO(self.__file.read(self._end - self._start + 1)) - # Return the file directly to allow WSGI server to use sendfile optimization - # Note: When returning the file directly, it will be closed by the WSGI server return self.__file diff --git a/tests_integrity/test_response_closed.py b/tests_integrity/test_response_closed.py new file mode 100644 index 0000000..e82e690 --- /dev/null +++ b/tests_integrity/test_response_closed.py @@ -0,0 +1,34 @@ +"""Integrity test for closed buffer handling.""" +from os.path import dirname, join, pardir + +from pytest import fixture +from requests import Session + +from .support import check_url, start_server + +# pylint: disable=missing-function-docstring +# pylint: disable=redefined-outer-name + + +@fixture(scope="module") +def url(request): + """URL (server fixture in fact).""" + process = start_server( + request, + join(dirname(__file__), pardir, 'examples/response_closed.py')) + + yield "http://localhost:8080" # server is running + process.kill() + process.wait() + + +class TestResponseClosed: + """Test for Response with closed buffer handling.""" + + def test_simple_response(self, url): + """Test that a simple response works normally.""" + check_url(url+"/test") + + def test_response_data_access(self, url): + """Test that accessing data property before sending works.""" + check_url(url+"/test-after-response") From 413804671c521bd19cac2463ffc36047d9fd221d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 08:46:41 +0000 Subject: [PATCH 08/12] Fix linting errors: indentation and unused imports - Fix E128 flake8 errors: proper continuation line indentation - Fix F401 flake8 error: remove unused Session import - Fix W0613 pylint errors: add pylint disable for unused arguments - Fix W0125 pylint errors: add pylint disable for runtime closed checks - All linting checks now pass Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- examples/response_closed.py | 4 ++-- poorwsgi/response.py | 12 ++++++------ tests_integrity/test_response_closed.py | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/response_closed.py b/examples/response_closed.py index 5121acf..ad9c1b0 100644 --- a/examples/response_closed.py +++ b/examples/response_closed.py @@ -8,7 +8,7 @@ @app.route("/test") -def test_handler(req): +def test_handler(req): # pylint: disable=unused-argument """Simple test handler that returns a Response.""" res = Response("Test data") # After response is sent, the buffer will be closed by WSGI server @@ -17,7 +17,7 @@ def test_handler(req): @app.route("/test-after-response") -def test_after_response(req): +def test_after_response(req): # pylint: disable=unused-argument """Handler that tries to access response data after creation.""" res = Response("Test data") # Try to access data property immediately (should work) diff --git a/poorwsgi/response.py b/poorwsgi/response.py index 532793e..ef8ad5c 100644 --- a/poorwsgi/response.py +++ b/poorwsgi/response.py @@ -356,9 +356,9 @@ def __init__(self, data: Union[str, bytes] = b'', @property def data(self): - if self.__buffer.closed: + if self.__buffer.closed: # pylint: disable=using-constant-test log.warning("Attempt to access data from closed buffer. " - "Response has likely been sent already.") + "Response has likely been sent already.") return b'' self.__buffer.seek(0) return self.__buffer.read() @@ -371,9 +371,9 @@ def write(self, data: Union[str, bytes]): self.__buffer.write(data) def __end_of_response__(self): - if self.__buffer.closed: + if self.__buffer.closed: # pylint: disable=using-constant-test log.error("Buffer is closed in __end_of_response__. " - "This should not happen.") + "This should not happen.") return IBytesIO(b'') self.__buffer.seek(self._start) if self._end: @@ -512,7 +512,7 @@ def data(self): """ if self.__file.closed: log.warning("Attempt to access data from closed file. " - "Response has likely been sent already.") + "Response has likely been sent already.") return b'' if self.__file.seekable(): self.__file.seek(self.__pos) @@ -529,7 +529,7 @@ def __end_of_response__(self): """ if self.__file.closed: log.error("File is closed in __end_of_response__. " - "This should not happen.") + "This should not happen.") return IBytesIO(b'') if self.__file.seekable(): self.__file.seek(self._start) diff --git a/tests_integrity/test_response_closed.py b/tests_integrity/test_response_closed.py index e82e690..6fa9ed8 100644 --- a/tests_integrity/test_response_closed.py +++ b/tests_integrity/test_response_closed.py @@ -2,7 +2,6 @@ from os.path import dirname, join, pardir from pytest import fixture -from requests import Session from .support import check_url, start_server From 1de76126a42d78ae7e08cdb6fba5c5d692c632b1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 08:55:54 +0000 Subject: [PATCH 09/12] Use underscore for unused arguments instead of pylint disable - Replace `req` parameter with `_` in example handlers - Remove pylint disable comments for unused-argument - Cleaner code following Python conventions Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- examples/response_closed.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/response_closed.py b/examples/response_closed.py index ad9c1b0..17a218f 100644 --- a/examples/response_closed.py +++ b/examples/response_closed.py @@ -8,7 +8,7 @@ @app.route("/test") -def test_handler(req): # pylint: disable=unused-argument +def test_handler(_): """Simple test handler that returns a Response.""" res = Response("Test data") # After response is sent, the buffer will be closed by WSGI server @@ -17,7 +17,7 @@ def test_handler(req): # pylint: disable=unused-argument @app.route("/test-after-response") -def test_after_response(req): # pylint: disable=unused-argument +def test_after_response(_): """Handler that tries to access response data after creation.""" res = Response("Test data") # Try to access data property immediately (should work) From 75b8e5abf05fbf0ecbeaf192e46f1edbe8d484fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 09:05:00 +0000 Subject: [PATCH 10/12] Fix pytest collection error: rename test_ functions in example - Rename test_handler to handler - Rename test_after_response to handler_after_response - Functions starting with test_ are collected by pytest as tests - Using _ as parameter name caused pytest fixture lookup error - Keep _ as parameter for unused req argument (matches other examples) Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- examples/response_closed.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/response_closed.py b/examples/response_closed.py index 17a218f..98e9e2c 100644 --- a/examples/response_closed.py +++ b/examples/response_closed.py @@ -8,7 +8,7 @@ @app.route("/test") -def test_handler(_): +def handler(_): """Simple test handler that returns a Response.""" res = Response("Test data") # After response is sent, the buffer will be closed by WSGI server @@ -17,7 +17,7 @@ def test_handler(_): @app.route("/test-after-response") -def test_after_response(_): +def handler_after_response(_): """Handler that tries to access response data after creation.""" res = Response("Test data") # Try to access data property immediately (should work) From 831704e915dd4cd26a5f9237e8c61020691beb88 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 09:24:01 +0000 Subject: [PATCH 11/12] Fix uwsgi application lookup: add application alias - Add `application` alias for uwsgi compatibility - uwsgi looks for 'application' callable by default - All other examples use: app = application = Application(...) - Tests now pass with --with-uwsgi flag Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- examples/response_closed.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/response_closed.py b/examples/response_closed.py index 98e9e2c..363a759 100644 --- a/examples/response_closed.py +++ b/examples/response_closed.py @@ -4,7 +4,7 @@ from poorwsgi import Application from poorwsgi.response import Response -app = Application("response_closed") +app = application = Application("response_closed") @app.route("/test") From e19daa7f1a1a511878aa9f6560d52e57c1dd9354 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 8 Jan 2026 12:51:15 +0000 Subject: [PATCH 12/12] Add Python path setup for standalone example execution - Add sys.path manipulation to run example without installed package - Follows pattern from other examples (metrics.py, etc.) - Allows examples to import poorwsgi from parent directory - Add pylint disable for wrong-import-position - Add noqa comments for imports after path modification Co-authored-by: ondratu <6469029+ondratu@users.noreply.github.com> --- examples/response_closed.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/examples/response_closed.py b/examples/response_closed.py index 363a759..e42b10a 100644 --- a/examples/response_closed.py +++ b/examples/response_closed.py @@ -1,8 +1,16 @@ #!/usr/bin/env python3 """Example demonstrating Response with closed buffer scenario.""" +from sys import path as python_path -from poorwsgi import Application -from poorwsgi.response import Response +import os + +EXAMPLES_PATH = os.path.dirname(__file__) +python_path.insert(0, os.path.abspath( + os.path.join(EXAMPLES_PATH, os.path.pardir))) + +# pylint: disable=wrong-import-position +from poorwsgi import Application # noqa +from poorwsgi.response import Response # noqa app = application = Application("response_closed")