From b033fd2c521e953c9a2922bb9a3d8a163fe47cd9 Mon Sep 17 00:00:00 2001 From: Sam Mirazi Date: Tue, 3 Jun 2025 12:50:33 -0700 Subject: [PATCH] 26 --- .gitignore | 6 + Design Docs/TDD.md | 2 - README.md | 39 +++- requirements.txt | 1 + ...est_benchmark.cpython-312-pytest-8.3.5.pyc | Bin 20511 -> 0 bytes ...fastapi_route.cpython-312-pytest-8.3.5.pyc | Bin 5995 -> 0 bytes .../test_fastapi_route.cpython-312.pyc | Bin 4569 -> 0 bytes ...t_flask_route.cpython-312-pytest-8.3.5.pyc | Bin 4586 -> 0 bytes tests/test_benchmark.py | 179 ------------------ tests/test_fastapi_route.py | 66 ------- tests/test_flask_route.py | 49 ----- 11 files changed, 42 insertions(+), 300 deletions(-) delete mode 100644 tests/__pycache__/test_benchmark.cpython-312-pytest-8.3.5.pyc delete mode 100644 tests/__pycache__/test_fastapi_route.cpython-312-pytest-8.3.5.pyc delete mode 100644 tests/__pycache__/test_fastapi_route.cpython-312.pyc delete mode 100644 tests/__pycache__/test_flask_route.cpython-312-pytest-8.3.5.pyc delete mode 100644 tests/test_benchmark.py delete mode 100644 tests/test_fastapi_route.py delete mode 100644 tests/test_flask_route.py diff --git a/.gitignore b/.gitignore index acc4d80..a1b3530 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ ENV/ .idea/ .vscode/ *.swp +.cursor/ # OS-specific .DS_Store @@ -40,3 +41,8 @@ dist/ # Other misc *.pem + +# Auto-generated documentation files +Design Docs/File_Tree.txt +Design Docs/ +SOP/ diff --git a/Design Docs/TDD.md b/Design Docs/TDD.md index f1c922a..dfb7208 100644 --- a/Design Docs/TDD.md +++ b/Design Docs/TDD.md @@ -1,5 +1,3 @@ -ControlAltPete — 5/28/2025 1:38 AM - Pythonistas: Your challenge for this week is... write a Python Flask web server that can handle requests for a home page at @route('/') and will simulate a slow request with time.sleep(3) then return a sample HTML page. Also write a Python FastAPI web server that handles the same request and does asyncio.sleep(3) then returns a sample HTML page. Finally write a script (Python or shell) to request 100 pages from your web server on http://localhost and time how long it takes for your two different web servers to handle all 100 requests. diff --git a/README.md b/README.md index 13a392d..0a3ab4c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # FastAPI vs Flask Performance Benchmark This project provides a benchmarking suite to compare the performance of FastAPI and Flask web frameworks under various conditions, including scenarios with and without artificial delays. +It originated from a challenge to build minimal Flask and FastAPI web servers, each with an artificial 3-second delay (later adjusted), and then benchmark them by sending a configurable number of requests to observe performance differences. ## Project Overview @@ -23,10 +24,8 @@ Hacker Dojo/ │ └── Flask_with_delay.py ├── benchmark/ # Core benchmark script │ └── run_benchmark.py -├── Design Docs/ # (Placeholder for design documents) +├── Design Docs/ # Project design documents (e.g., TDD.md, ignored by Git) ├── SOP/ # Standard Operating Procedures & Handoff notes -├── static/ # (Placeholder for static assets) -├── tests/ # (Placeholder for automated tests) ├── .gitignore # Files and directories ignored by Git ├── README.md # This file ├── requirements.txt # Python dependencies @@ -36,9 +35,20 @@ Hacker Dojo/ ## Prerequisites -- Python 3.8+ +- Python 3.8+ (developed and tested with Python 3.11/3.12) - Git +## Key Technologies Used + +- **Python**: Core language for servers and scripting. +- **Flask**: Synchronous web framework. +- **FastAPI**: Asynchronous web framework. +- **Uvicorn**: ASGI server for FastAPI. +- **Werkzeug**: WSGI toolkit and development server for Flask. +- **httpx**: HTTP client for benchmarks. +- **pytest**: Testing framework (see "Development and Testing" section). +- **Rich**: For formatted table output. + ## Setup and Installation 1. **Clone the repository:** @@ -91,6 +101,27 @@ This project provides two main scripts to run benchmarks: - Ensure no other applications are using ports 3000 (for Flask) or 8000 (for FastAPI) when running the benchmarks. The scripts attempt to manage server processes, but conflicts can still occur. - Benchmark results can be influenced by your system's hardware, OS, and current load. For the most accurate comparisons, run benchmarks in a consistent environment. +## Development and Testing + +This project was initially developed following a Test-Driven Development (TDD) methodology. The general approach involved: + +1. Writing failing tests for specific routes or functionalities (Red). +2. Implementing the minimal code required to make the tests pass (Green). +3. Refactoring the code for clarity and efficiency (Refactor). + +The `Design Docs/TDD.md` file (which is not part of the Git repository) contains the original detailed phased plan and example tests using `pytest` and `httpx` for: + +- Basic Flask and FastAPI route functionality. +- Benchmark harness integration. + +While the dedicated `tests/` directory and its initial test files were removed during the project's evolution to focus on the benchmarking scripts, the `TDD.md` serves as a valuable reference for understanding the original testing strategy or for re-introducing a test suite. + +If a test suite is re-established (e.g., in a `tests/` directory), tests would typically be run using a command like: + +```bash +pytest +``` + ## Interpreting Results The benchmark scripts will output tables summarizing: diff --git a/requirements.txt b/requirements.txt index 09bc532..dfbd8e2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,6 +5,7 @@ pytest==8.3.5 httpx==0.28.1 requests==2.32.3 rich==14.0.0 +pytest-asyncio==1.0.0 # Optional, as per TDD plan: # tenacity diff --git a/tests/__pycache__/test_benchmark.cpython-312-pytest-8.3.5.pyc b/tests/__pycache__/test_benchmark.cpython-312-pytest-8.3.5.pyc deleted file mode 100644 index 04c84b9d9d34a48026189823eb06074f7b197795..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20511 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 diff --git a/tests/__pycache__/test_fastapi_route.cpython-312.pyc b/tests/__pycache__/test_fastapi_route.cpython-312.pyc deleted file mode 100644 index f484d075d32a8e74dae8e6f121074e3b04324444..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4569 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 float: - \"\"\" - Runs the benchmark script for the given framework and returns the reported time. - Raises RuntimeError if the script fails or output cannot be parsed. - \"\"\" - if not os.path.exists(BENCHMARK_SCRIPT_PATH): - raise FileNotFoundError(f"Benchmark script not found at {BENCHMARK_SCRIPT_PATH}") - - command = [sys.executable, BENCHMARK_SCRIPT_PATH, framework_name] - - try: - # Ensure the script is run from the project root for consistent module resolution if any - project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) - process = subprocess.run( - command, - capture_output=True, - text=True, - check=True, - timeout=300, # 5 minutes timeout, adjust as needed - cwd=project_root - ) - output = process.stdout - # print(f"--- Benchmark output for {framework_name} ---\\n{output}\\n-----------------------------------------") # For debugging - - # Regex to find "X.XX seconds." and capture X.XX - # Example line: "Flask benchmark: 100/100 successful requests in 15.32 seconds." - match = re.search(r"(\\d+\\.\\d{2}) seconds\\.", output) - if match: - return float(match.group(1)) - else: - raise ValueError( - f"Could not parse execution time from benchmark output for {framework_name}.\\nOutput:\\n{output}" - ) - except FileNotFoundError: # Should be caught by the initial check, but as a safeguard for command itself - raise RuntimeError(f"Python executable not found at {sys.executable} or script path incorrect.") - except subprocess.CalledProcessError as e: - raise RuntimeError( - f"Benchmark script for {framework_name} failed with exit code {e.returncode}.\\n" - f"Stdout:\\n{e.stdout}\\n" - f"Stderr:\\n{e.stderr}" - ) - except subprocess.TimeoutExpired as e: - raise RuntimeError( - f"Benchmark script for {framework_name} timed out after {e.timeout} seconds.\\n" - f"Stdout:\\n{e.stdout}\\n" - f"Stderr:\\n{e.stderr}" - ) - except ValueError as e: # Re-raise specific parsing error - raise e - except Exception as e: # Catch any other unexpected errors - raise RuntimeError(f"An unexpected error occurred while running benchmark for {framework_name}: {e}") - - -# --- Test Cases --- - -# Note: These tests will run the actual servers and benchmark script. -# Ensure the Flask and FastAPI applications (app_flask/flask_application.py and app_fastapi/app.py) -# are correctly implemented to listen on FLASK_URL and FASTAPI_URL specified in run_benchmark.py -# (http://127.0.0.1:3000/ and http://127.0.0.1:8000/ respectively). -# The tests also depend on the external Flask and FastAPI servers being started by the benchmark script's logic -# (implicitly, as the benchmark script itself doesn't start servers; it assumes they are running). -# This is a deviation from the unit tests which start servers. -# The benchmark script ITSELF is what's being tested here, not the servers ability to start. -# The benchmark script expects servers to be ALREADY running at the specified URLs. - -# THIS IS A PROBLEM: The benchmark script `run_benchmark.py` does NOT start the servers. -# The tests in test_flask_route.py and test_fastapi_route.py DO start the servers. -# The TDD for Phase 5 benchmark harness does not explicitly state the benchmark script should start servers, -# but the integration tests for the benchmark WILL require servers to be running. - -# For these integration tests to work as intended by the TDD (testing the benchmark script), -# we will need to: -# 1. Start the Flask server. -# 2. Run the Flask benchmark script and get its time. -# 3. Stop the Flask server. -# 4. Start the FastAPI server. -# 5. Run the FastAPI benchmark script and get its time. -# 6. Stop the FastAPI server. -# This makes the test_benchmark.py more complex as it needs to manage server processes -# similar to how test_flask_route.py and test_fastapi_route.py do. - -# Let's adjust `run_benchmark_script` to accept server management functions or integrate them. -# For now, I will write the tests assuming the servers are MANUALLY started before running pytest for these. -# This is a point to clarify or improve based on TDD Phase 5 goals. -# The TDD says: "Assert that Flask total time > 3 seconds" etc. -# This implies the benchmark script is run against live servers. - - -# Global variables to store benchmark times to avoid re-running for each assertion if not necessary -# However, for true isolation and to ensure each test measures fresh, it's better to run the benchmark for each. -# Let's run it each time for now. A setup_module could optimize later if too slow. - -FLASK_BENCH_TIME = -1.0 -FASTAPI_BENCH_TIME = -1.0 - -# Pytest markers for skipping if servers aren't intended to be up, or for explicit server setup. -# For now, these tests will FAIL if servers are not running at the expected ports. - -@pytest.mark.benchmark_integration # Custom marker -def test_flask_benchmark_timing(): - \"\"\"Tests the Flask benchmark timing.\"\"\" - global FLASK_BENCH_TIME - # This requires the Flask server to be running on http://127.0.0.1:3000/ - # AND the FastAPI server on http://127.0.0.1:8000/ (though not directly used by this specific call) - # because the benchmark script itself doesn't conditionally import/run parts. - # Actually, run_benchmark.py only needs the specific server it's targeting. - - print("\\nRunning Flask benchmark for timing test...") - flask_time = run_benchmark_script("flask") - FLASK_BENCH_TIME = flask_time # Store for potential use in other tests - print(f"Flask benchmark reported: {flask_time:.2f}s") - - # TDD: Assert that Flask total time > 3 seconds (actual time depends on concurrency) - # Given 100 requests and a 3s delay each, serially it's 300s. - # With ThreadPoolExecutor, it's much less but still significant. - # A single request is 3s. For 100 requests, even with threads, it must be > 3s. - # It should be substantially more if NUM_REQUESTS / num_cores > a few cycles of 3s. - # For 100 requests, if 8 cores, roughly 100/8 * 3 = 12.5 * 3 = ~37.5s (very rough). - # The TDD example shows "Flask (100 req): 18.4 s". - assert flask_time > 3.0, "Flask benchmark time should be greater than the artificial 3s delay." - # A more realistic lower bound for 100 requests might be NUM_REQUESTS * 3 / MAX_POSSIBLE_CONCURRENCY_FACTOR - # For now, TDD's "> 3s" is the primary guide, but we expect it to be higher. - - -@pytest.mark.benchmark_integration -def test_fastapi_benchmark_timing(): - \"\"\"Tests the FastAPI benchmark timing.\"\"\" - global FASTAPI_BENCH_TIME - # This requires the FastAPI server to be running on http://127.0.0.1:8000/ - print("\\nRunning FastAPI benchmark for timing test...") - fastapi_time = run_benchmark_script("fastapi") - FASTAPI_BENCH_TIME = fastapi_time - print(f"FastAPI benchmark reported: {fastapi_time:.2f}s") - - # TDD: Assert that FastAPI total time ≈ 3–4 seconds (more truly concurrent) - assert 2.9 < fastapi_time < 5.0, "FastAPI benchmark time should be close to 3-4s (e.g., 2.9s to 5s for tolerance)." - # The TDD example shows "FastAPI(100 req): 3.7 s" - -@pytest.mark.benchmark_integration -def test_fastapi_is_faster_than_flask(): - \"\"\"Tests that FastAPI benchmark is faster than Flask benchmark.\"\"\" - # Re-run benchmarks to ensure fresh comparison unless already run and stored - # For reliability, let's re-run, though it adds to test time. - print("\\nRe-running Flask benchmark for comparison...") - flask_comparison_time = run_benchmark_script("flask") - print(f"Flask benchmark for comparison reported: {flask_comparison_time:.2f}s") - - print("\\nRe-running FastAPI benchmark for comparison...") - fastapi_comparison_time = run_benchmark_script("fastapi") - print(f"FastAPI benchmark for comparison reported: {fastapi_comparison_time:.2f}s") - - # TDD: Add a test test_fastapi_faster(): assert fast_time < flask_time - assert fastapi_comparison_time < flask_comparison_time, "FastAPI should be significantly faster than Flask for this benchmark." - -# To run these tests: -# 1. Ensure Flask server (app_flask/flask_application.py) can be started on http://127.0.0.1:3000/ -# 2. Ensure FastAPI server (app_fastapi/app.py) can be started on http://127.0.0.1:8000/ -# 3. Manually start BOTH servers in separate terminals before running pytest. -# - Terminal 1: python app_flask/flask_application.py -# - Terminal 2: uvicorn app_fastapi.app:app --host 127.0.0.1 --port 8000 -# 4. Then run: pytest tests/test_benchmark.py -m benchmark_integration -s -v -# (-s for stdout, -v for verbose, -m to pick only these tests) - -# Future improvement: -# - Use pytest fixtures (e.g., session-scoped) to start/stop the Flask and FastAPI servers -# automatically before and after these benchmark tests run. This would make them self-contained. -# This would involve adapting the server-starting logic from test_flask_route.py and test_fastapi_route.py. -# For example, a fixture could yield a server process, and the tests would use that. \ No newline at end of file diff --git a/tests/test_fastapi_route.py b/tests/test_fastapi_route.py deleted file mode 100644 index f6e646a..0000000 --- a/tests/test_fastapi_route.py +++ /dev/null @@ -1,66 +0,0 @@ -# tests/test_fastapi_route.py -import httpx -import asyncio -import pytest # For @pytest.mark.asyncio -import uvicorn -import threading -import time -from multiprocessing import Process # Using Process for better isolation -import os - -# Server configuration -HOST = "127.0.0.1" -PORT = 8000 - -class UvicornServer(threading.Thread): - def __init__(self, app_module_str): - super().__init__(daemon=True) - self.app_module_str = app_module_str - self.server_started = threading.Event() - self.config = uvicorn.Config(app_module_str, host=HOST, port=PORT, log_level="info") - self.server = uvicorn.Server(config=self.config) - - def run(self): - # Need to set a new event loop for the thread if running asyncio components - # For uvicorn.Server.serve(), it typically manages its own loop or integrates - # with an existing one if started from an async context. - # However, running it in a separate thread needs care. - # Simpler: uvicorn.run() which handles loop creation. - uvicorn.run(self.app_module_str, host=HOST, port=PORT, log_level="warning") # log_level warning to reduce noise - - # UvicornServer using Process for cleaner start/stop - # This might be more robust for test isolation. - -def run_server_process(app_module_str, host, port, project_root_dir): - # Add project root to Python path for the new process - import sys - sys.path.insert(0, project_root_dir) - uvicorn.run(app_module_str, host=host, port=port, log_level="warning") - -async def start_server_fastapi(): - project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) - # Using Process to run Uvicorn. This provides better isolation and cleanup. - proc = Process(target=run_server_process, args=("app_fastapi.app:app", HOST, PORT, project_root), daemon=True) - proc.start() - await asyncio.sleep(2.0) # Increased sleep to ensure server is fully up - if not proc.is_alive(): - raise RuntimeError("FastAPI server process failed to start.") - return proc # Return the process object - -@pytest.mark.asyncio -async def test_home_returns_html_fastapi(): - server_process = await start_server_fastapi() - try: - async with httpx.AsyncClient(timeout=10) as client: - r = await client.get(f"http://{HOST}:{PORT}/") - assert r.status_code == 200 - assert "

FastAPI Server: 3-Seconds Artificial Delay Demo

" in r.text # Expected content - finally: - if server_process and server_process.is_alive(): - server_process.terminate() # Send SIGTERM - server_process.join(timeout=5) # Wait for termination - if server_process.is_alive(): # Still alive after timeout - print("FastAPI server process did not terminate gracefully, killing.") - server_process.kill() # Force kill - server_process.join(timeout=5) # Wait for kill - # print("FastAPI server process stopped.") # Optional debug \ No newline at end of file diff --git a/tests/test_flask_route.py b/tests/test_flask_route.py deleted file mode 100644 index 1065743..0000000 --- a/tests/test_flask_route.py +++ /dev/null @@ -1,49 +0,0 @@ -import httpx -import subprocess, time, os, signal - -def start_server(): - current_env = os.environ.copy() - project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) - script_path = os.path.join(project_root, "app_flask", "flask_application.py") - proc = subprocess.Popen( - ["python", script_path], - env=current_env, - cwd=project_root, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE - ) - time.sleep(1.5) - return proc - -def test_home_returns_html(): - proc = start_server() - server_stdout_data = b"" - server_stderr_data = b"" - try: - headers = { - "Cache-Control": "no-cache, no-store, must-revalidate", - "Pragma": "no-cache", - "Expires": "0" - } - r = httpx.get("http://127.0.0.1:3000/", timeout=10, headers=headers) - assert r.status_code == 200 - assert "

Flask Server: 3-Seconds Artificial Delay Demo

" in r.text - finally: - proc.terminate() - try: - stdout_bytes, stderr_bytes = proc.communicate(timeout=5) - server_stdout_data = stdout_bytes - server_stderr_data = stderr_bytes - except subprocess.TimeoutExpired: - print("Server did not terminate/communicate gracefully, killing.") - proc.kill() - try: - stdout_bytes, stderr_bytes = proc.communicate(timeout=1) - server_stdout_data = stdout_bytes - server_stderr_data = stderr_bytes - except subprocess.TimeoutExpired: - print("Could not get output even after kill.") - - # Keep these for CI logs or if needed later, but they will be empty if flask app is quiet. - # print(f"Server stdout captured:\n{server_stdout_data.decode(errors='replace')}") - # print(f"Server stderr captured:\n{server_stderr_data.decode(errors='replace')}") \ No newline at end of file -- 2.25.1