diff --git a/frameworks/Python/nexios/READEME.md b/frameworks/Python/nexios/READEME.md new file mode 100644 index 00000000000..65119ca8151 --- /dev/null +++ b/frameworks/Python/nexios/READEME.md @@ -0,0 +1,139 @@ + +## `NEXIOS` + +
+ +Typing SVG + +

+ + Support +

+

Nexios 2.4.x

+ +
+

+ + +

+ Python Version + Downloads + Contributions + Active Development +

+ +

+ + + + + + +
+ +

Star the repo if u like it🌟 +

+ +Nexios is a high-performance Python web framework. Designed for speed, flexibility, and simplicity, Nexios delivers exceptional performance through its native Rust engine while maintaining the simplicity and elegance of Python. It supports RESTful APIs, authentication, and integrates easily with any ORM. Built for modern web development, Nexios allows developers to quickly spin up scalable, modular apps with minimal boilerplate—ideal for startups, rapid prototyping, and custom backend solutions. Think Django's capability with Rust-powered speed. + +--- + + +## `Installation` 📦 + +To install **Nexios**, you can use several methods depending on your environment and preferred package manager. Below are the instructions for different package managers: + +### 1. **From `pip`** (Standard Python Package Manager) + +```bash +pip install nexios +``` + + +## Features ✨ + +- [x] **Routing** +- [x] **Automatic OpenAPI Documentation** +- [x] **Session Management** +- [x] **File Router** +- [x] **Authentication (Limited)** +- [x] **Event Listener for Signals** +- [x] **Middleware Support** +- [x] **Express-like Functionality** +- [x] **JWT Authentication** +- [x] **Pydantic Support** +- [x] **Dependency Injection** +- [x] **In-built Support for CORS** +- [x] **Custom Decorators** +- [x] **WebSocket Support** +- [x] **Custom Error Handling** +- [x] **Pagination** +- [x] **HTTP/2 Support** +- [x] **High-Performance Async Processing** + +### Upcoming Features + +- [ ] **Inbuilt Database ORM Integration** +- [ ] **Asynchronous Task Queue** +- [ ] **Rate Limiting** +- [ ] **API Throttling** + +### Basic Example + +```py +from nexios import NexiosApp +from nexios.http import Request, Response + +app = NexiosApp() + +@app.get("/") +async def basic(request: Request, response: Response): + return {"message": "Hello, world!"} + # return response.json({"message":"Hello, world!"}) ## This will work for more control + + +``` + +### Another Basic Example + +```py +from nexios import NexiosApp, Depend +from nexios.http import Request, Response + +app = NexiosApp() + +async def get_user(): + return {"name": "John Doe"} + + +@app.get("/users") +async def get_user(request: Request, response: Response, user: Depend(get_user)): + + return {"user": user} +``` + +Visit http://localhost:4000/docs to view the Swagger API documentation. + + + +### Testimonies + +> "Adopting Nexios at our startup has been a practical and effective choice. In a fast-moving development environment, we needed something lightweight and efficient — Nexios met that need. +> +> Its clean architecture and compatibility with different ORMs helped our team work more efficiently and keep things maintainable. One of its strengths is how straightforward it is — minimal overhead, just the tools we need to build and scale our backend services. +> +> Credit to Dunamis for introducing Nexios to the team. It’s now a steady part of our stack, and it’s serving us well. +> — Joseph Mmadubuike , Chief Technology Officer buzzbuntu.com + +## See the full docs + +👉 https://nexios-docs.netlify.app + +## Contributors: + + + + + + + diff --git a/frameworks/Python/nexios/README.md b/frameworks/Python/nexios/README.md new file mode 100644 index 00000000000..5b76f573d17 --- /dev/null +++ b/frameworks/Python/nexios/README.md @@ -0,0 +1,61 @@ +# Nexios Framework Benchmark + +This directory contains the Nexios framework implementation for the TechEmpower Web Framework Benchmarks. + +## About Nexios + +Nexios is a modern, high-performance ASGI web framework that combines high performance with developer-friendly features. Built on proven design patterns while introducing modern capabilities like dependency injection, automatic OpenAPI documentation, and comprehensive middleware support. + +## Test Implementations + +This benchmark includes multiple server configurations: + +- **default**: Nexios with Uvicorn (standard configuration) +- **gunicorn**: Nexios with Gunicorn server +- **uvicorn**: Nexios with Uvicorn ASGI server +- **granian**: Nexios with Granian server (Rust-based ASGI server) +- **socketify-asgi**: Nexios with Socketify ASGI server (C++-based) + +## Benchmark Tests + +- **JSON**: Simple JSON serialization +- **DB**: Single database query +- **Queries**: Multiple database queries (1-500) +- **Fortunes**: HTML template rendering with database data +- **Updates**: Database updates with multiple queries +- **Plaintext**: Simple text response + +## Running the Benchmarks + +```bash +# Build all images +./run_benchmarks.sh + +# Run individual containers +docker run -p 8080:8080 nexios-gunicorn +docker run -p 8080:8080 nexios-uvicorn +docker run -p 8080:8080 nexios-granian +docker run -p 8080:8080 nexios-socketify +``` + +## Configuration + +- **Database**: PostgreSQL with connection pooling +- **Workers**: CPU count optimized +- **Port**: 8080 (standard for TechEmpower) +- **Host**: 0.0.0.0 (bind to all interfaces) +- **Logging**: Error level only for performance + +## Framework Comparison + +Nexios vs FastAPI: +- Similar ASGI foundation +- Built-in dependency injection +- Automatic OpenAPI documentation +- Comprehensive middleware system +- WebSocket support +- Pydantic integration + +## License + +This benchmark implementation is part of the TechEmpower Web Framework Benchmarks project. \ No newline at end of file diff --git a/frameworks/Python/nexios/app-granian.py b/frameworks/Python/nexios/app-granian.py new file mode 100644 index 00000000000..6aea8704dfc --- /dev/null +++ b/frameworks/Python/nexios/app-granian.py @@ -0,0 +1,158 @@ +import multiprocessing +import os +import random +from contextlib import asynccontextmanager + +import asyncpg +from nexios import NexiosApp, MakeConfig + + + +READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' +WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' +ADDITIONAL_ROW = [0, "Additional fortune added at request time."] +MAX_POOL_SIZE = 1000 // multiprocessing.cpu_count() +MIN_POOL_SIZE = max(int(MAX_POOL_SIZE / 2), 1) + +connection_pool = None + + +def get_num_queries(queries): + try: + query_count = int(queries) + except (ValueError, TypeError): + return 1 + + if query_count < 1: + return 1 + if query_count > 500: + return 500 + return query_count + + +async def setup_database(): + return await asyncpg.create_pool( + user=os.getenv("PGUSER", "benchmarkdbuser"), + password=os.getenv("PGPASS", "benchmarkdbpass"), + database="hello_world", + host="tfb-database", + port=5432, + min_size=MIN_POOL_SIZE, + max_size=MAX_POOL_SIZE, + ) + + +@asynccontextmanager +async def lifespan(app: NexiosApp): + # Setup the database connection pool + global connection_pool + connection_pool = await setup_database() + yield + # Close the database connection pool + await connection_pool.close() + + +# Create Nexios app with lifespan optimized for Granian +app = NexiosApp( + config=MakeConfig({ + "debug": False, + "openapi": { + "enabled": False + } + }), + lifespan=lifespan +) + + +@app.get("/json") +async def json_serialization(request, response): + return response.json({"message": "Hello, world!"}) + + +@app.get("/db") +async def single_database_query(request, response): + row_id = random.randint(1, 10000) + async with connection_pool.acquire() as connection: + number = await connection.fetchval(READ_ROW_SQL, row_id) + + return response.json({"id": row_id, "randomNumber": number}) + + +@app.get("/queries") +async def multiple_database_queries(request, response): + queries = request.query_params.get("queries") + num_queries = get_num_queries(queries) + row_ids = random.sample(range(1, 10000), num_queries) + worlds = [] + + async with connection_pool.acquire() as connection: + statement = await connection.prepare(READ_ROW_SQL) + for row_id in row_ids: + number = await statement.fetchval(row_id) + worlds.append({"id": row_id, "randomNumber": number}) + + return response.json(worlds) + + +@app.get("/fortunes") +async def fortunes(request, response): + async with connection_pool.acquire() as connection: + fortunes = await connection.fetch("SELECT * FROM Fortune") + + fortunes.append(ADDITIONAL_ROW) + fortunes.sort(key=lambda row: row[1]) + + # Render fortune template + html_content = """ + + + Fortunes + + + + """ + + for fortune in fortunes: + html_content += f"" + + html_content += """ +
idmessage
{fortune[0]}{fortune[1]}
+ + + """ + + return response.html(html_content) + + +@app.get("/updates") +async def database_updates(request, response): + queries = request.query_params.get("queries") + num_queries = get_num_queries(queries) + # To avoid deadlock + ids = sorted(random.sample(range(1, 10000 + 1), num_queries)) + numbers = sorted(random.sample(range(1, 10000), num_queries)) + updates = list(zip(ids, numbers)) + + worlds = [ + {"id": row_id, "randomNumber": number} for row_id, number in updates + ] + + async with connection_pool.acquire() as connection: + statement = await connection.prepare(READ_ROW_SQL) + for row_id, _ in updates: + await statement.fetchval(row_id) + await connection.executemany(WRITE_ROW_SQL, updates) + + return response.json(worlds) + + +@app.get("/plaintext") +async def plaintext(request, response): + return response.text("Hello, world!") + + +if __name__ == "__main__": + # Granian will be called from the command line + # This is just for development/testing + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8080) \ No newline at end of file diff --git a/frameworks/Python/nexios/app-gunicorn.py b/frameworks/Python/nexios/app-gunicorn.py new file mode 100644 index 00000000000..296e52bfac0 --- /dev/null +++ b/frameworks/Python/nexios/app-gunicorn.py @@ -0,0 +1,157 @@ +import multiprocessing +import os +import random +from contextlib import asynccontextmanager + +import asyncpg +from nexios import NexiosApp, MakeConfig + + +READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' +WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' +ADDITIONAL_ROW = [0, "Additional fortune added at request time."] +MAX_POOL_SIZE = 1000 // multiprocessing.cpu_count() +MIN_POOL_SIZE = max(int(MAX_POOL_SIZE / 2), 1) + +connection_pool = None + + +def get_num_queries(queries): + try: + query_count = int(queries) + except (ValueError, TypeError): + return 1 + + if query_count < 1: + return 1 + if query_count > 500: + return 500 + return query_count + + +async def setup_database(): + return await asyncpg.create_pool( + user=os.getenv("PGUSER", "benchmarkdbuser"), + password=os.getenv("PGPASS", "benchmarkdbpass"), + database="hello_world", + host="tfb-database", + port=5432, + min_size=MIN_POOL_SIZE, + max_size=MAX_POOL_SIZE, + ) + + +@asynccontextmanager +async def lifespan(app: NexiosApp): + # Setup the database connection pool + global connection_pool + connection_pool = await setup_database() + yield + # Close the database connection pool + await connection_pool.close() + + +# Create Nexios app with lifespan optimized for Gunicorn +app = NexiosApp( + config=MakeConfig({ + "debug": False, + "openapi": { + "enabled": False + } + }), + lifespan=lifespan +) + + +@app.get("/json") +async def json_serialization(request, response): + return response.json({"message": "Hello, world!"}) + + +@app.get("/db") +async def single_database_query(request, response): + row_id = random.randint(1, 10000) + async with connection_pool.acquire() as connection: + number = await connection.fetchval(READ_ROW_SQL, row_id) + + return response.json({"id": row_id, "randomNumber": number}) + + +@app.get("/queries") +async def multiple_database_queries(request, response): + queries = request.query_params.get("queries") + num_queries = get_num_queries(queries) + row_ids = random.sample(range(1, 10000), num_queries) + worlds = [] + + async with connection_pool.acquire() as connection: + statement = await connection.prepare(READ_ROW_SQL) + for row_id in row_ids: + number = await statement.fetchval(row_id) + worlds.append({"id": row_id, "randomNumber": number}) + + return response.json(worlds) + + +@app.get("/fortunes") +async def fortunes(request, response): + async with connection_pool.acquire() as connection: + fortunes = await connection.fetch("SELECT * FROM Fortune") + + fortunes.append(ADDITIONAL_ROW) + fortunes.sort(key=lambda row: row[1]) + + # Render fortune template + html_content = """ + + + Fortunes + + + + """ + + for fortune in fortunes: + html_content += f"" + + html_content += """ +
idmessage
{fortune[0]}{fortune[1]}
+ + + """ + + return response.html(html_content) + + +@app.get("/updates") +async def database_updates(request, response): + queries = request.query_params.get("queries") + num_queries = get_num_queries(queries) + # To avoid deadlock + ids = sorted(random.sample(range(1, 10000 + 1), num_queries)) + numbers = sorted(random.sample(range(1, 10000), num_queries)) + updates = list(zip(ids, numbers)) + + worlds = [ + {"id": row_id, "randomNumber": number} for row_id, number in updates + ] + + async with connection_pool.acquire() as connection: + statement = await connection.prepare(READ_ROW_SQL) + for row_id, _ in updates: + await statement.fetchval(row_id) + await connection.executemany(WRITE_ROW_SQL, updates) + + return response.json(worlds) + + +@app.get("/plaintext") +async def plaintext(request, response): + return response.text("Hello, world!") + + +if __name__ == "__main__": + # Gunicorn will be called from the command line + # This is just for development/testing + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8080) \ No newline at end of file diff --git a/frameworks/Python/nexios/app-socketify-asgi.py b/frameworks/Python/nexios/app-socketify-asgi.py new file mode 100644 index 00000000000..02cd07a2f6b --- /dev/null +++ b/frameworks/Python/nexios/app-socketify-asgi.py @@ -0,0 +1,177 @@ +import os +import multiprocessing +import logging +import random +from contextlib import asynccontextmanager + +import asyncpg +from nexios import NexiosApp, MakeConfig +from socketify import ASGI + + + +READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' +WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' +ADDITIONAL_ROW = [0, "Additional fortune added at request time."] +MAX_POOL_SIZE = 1000 // multiprocessing.cpu_count() +MIN_POOL_SIZE = max(int(MAX_POOL_SIZE / 2), 1) + +connection_pool = None + + +def get_num_queries(queries): + try: + query_count = int(queries) + except (ValueError, TypeError): + return 1 + + if query_count < 1: + return 1 + if query_count > 500: + return 500 + return query_count + + +async def setup_database(): + return await asyncpg.create_pool( + user=os.getenv("PGUSER", "benchmarkdbuser"), + password=os.getenv("PGPASS", "benchmarkdbpass"), + database="hello_world", + host="tfb-database", + port=5432, + min_size=MIN_POOL_SIZE, + max_size=MAX_POOL_SIZE, + ) + + +@asynccontextmanager +async def lifespan(app: NexiosApp): + # Setup the database connection pool + global connection_pool + connection_pool = await setup_database() + yield + # Close the database connection pool + await connection_pool.close() + + +# Create Nexios app with lifespan optimized for Socketify +app = NexiosApp( + config=MakeConfig({ + "debug": False, + "openapi": { + "enabled": False + } + }), + lifespan=lifespan +) + + +@app.get("/json") +async def json_serialization(request, response): + return response.json({"message": "Hello, world!"}) + + +@app.get("/db") +async def single_database_query(request, response): + row_id = random.randint(1, 10000) + async with connection_pool.acquire() as connection: + number = await connection.fetchval(READ_ROW_SQL, row_id) + + return response.json({"id": row_id, "randomNumber": number}) + + +@app.get("/queries") +async def multiple_database_queries(request, response): + queries = request.query_params.get("queries") + num_queries = get_num_queries(queries) + row_ids = random.sample(range(1, 10000), num_queries) + worlds = [] + + async with connection_pool.acquire() as connection: + statement = await connection.prepare(READ_ROW_SQL) + for row_id in row_ids: + number = await statement.fetchval(row_id) + worlds.append({"id": row_id, "randomNumber": number}) + + return response.json(worlds) + + +@app.get("/fortunes") +async def fortunes(request, response): + async with connection_pool.acquire() as connection: + fortunes = await connection.fetch("SELECT * FROM Fortune") + + fortunes.append(ADDITIONAL_ROW) + fortunes.sort(key=lambda row: row[1]) + + # Render fortune template + html_content = """ + + + Fortunes + + + + """ + + for fortune in fortunes: + html_content += f"" + + html_content += """ +
idmessage
{fortune[0]}{fortune[1]}
+ + + """ + + return response.html(html_content) + + +@app.get("/updates") +async def database_updates(request, response): + queries = request.query_params.get("queries") + num_queries = get_num_queries(queries) + # To avoid deadlock + ids = sorted(random.sample(range(1, 10000 + 1), num_queries)) + numbers = sorted(random.sample(range(1, 10000), num_queries)) + updates = list(zip(ids, numbers)) + + worlds = [ + {"id": row_id, "randomNumber": number} for row_id, number in updates + ] + + async with connection_pool.acquire() as connection: + statement = await connection.prepare(READ_ROW_SQL) + for row_id, _ in updates: + await statement.fetchval(row_id) + await connection.executemany(WRITE_ROW_SQL, updates) + + return response.json(worlds) + + +@app.get("/plaintext") +async def plaintext(request, response): + return response.text("Hello, world!") + + +_is_travis = os.environ.get('TRAVIS') == 'true' + +workers = int(multiprocessing.cpu_count()) +if _is_travis: + workers = 2 + +def run_app(): + ASGI(app).listen(8080, lambda config: logging.info(f"Listening on port http://localhost:{config.port} now\n")).run() + + +def create_fork(): + n = os.fork() + # n greater than 0 means parent process + if not n > 0: + run_app() + + +# fork limiting the cpu count - 1 +for i in range(1, workers): + create_fork() + +run_app() # run app on the main process too :) \ No newline at end of file diff --git a/frameworks/Python/nexios/app.py b/frameworks/Python/nexios/app.py new file mode 100644 index 00000000000..da0f1de95f5 --- /dev/null +++ b/frameworks/Python/nexios/app.py @@ -0,0 +1,156 @@ +import multiprocessing +import os +import random +from contextlib import asynccontextmanager + +import asyncpg +from nexios import NexiosApp, MakeConfig + + + +READ_ROW_SQL = 'SELECT "id", "randomnumber" FROM "world" WHERE id = $1' +WRITE_ROW_SQL = 'UPDATE "world" SET "randomnumber"=$1 WHERE id=$2' +ADDITIONAL_ROW = [0, "Additional fortune added at request time."] +MAX_POOL_SIZE = 1000 // multiprocessing.cpu_count() +MIN_POOL_SIZE = max(int(MAX_POOL_SIZE / 2), 1) + +connection_pool = None + + +def get_num_queries(queries): + try: + query_count = int(queries) + except (ValueError, TypeError): + return 1 + + if query_count < 1: + return 1 + if query_count > 500: + return 500 + return query_count + + +async def setup_database(): + return await asyncpg.create_pool( + user=os.getenv("PGUSER", "benchmarkdbuser"), + password=os.getenv("PGPASS", "benchmarkdbpass"), + database="hello_world", + host="tfb-database", + port=5432, + min_size=MIN_POOL_SIZE, + max_size=MAX_POOL_SIZE, + ) + + +@asynccontextmanager +async def lifespan(app: NexiosApp): + # Setup the database connection pool + global connection_pool + connection_pool = await setup_database() + yield + # Close the database connection pool + await connection_pool.close() + + +# Create Nexios app with lifespan +app = NexiosApp( + config=MakeConfig({ + "debug": False, + "openapi": { + "enabled": False + } + }), + lifespan=lifespan +) + + +@app.get("/json") +async def json_serialization(request, response): + return response.json({"message": "Hello, world!"}) + + +@app.get("/db") +async def single_database_query(request, response): + row_id = random.randint(1, 10000) + async with connection_pool.acquire() as connection: + number = await connection.fetchval(READ_ROW_SQL, row_id) + + return response.json({"id": row_id, "randomNumber": number}) + + +@app.get("/queries") +async def multiple_database_queries(request, response): + queries = request.query_params.get("queries") + num_queries = get_num_queries(queries) + row_ids = random.sample(range(1, 10000), num_queries) + worlds = [] + + async with connection_pool.acquire() as connection: + statement = await connection.prepare(READ_ROW_SQL) + for row_id in row_ids: + number = await statement.fetchval(row_id) + worlds.append({"id": row_id, "randomNumber": number}) + + return response.json(worlds) + + +@app.get("/fortunes") +async def fortunes(request, response): + async with connection_pool.acquire() as connection: + fortunes = await connection.fetch("SELECT * FROM Fortune") + + fortunes.append(ADDITIONAL_ROW) + fortunes.sort(key=lambda row: row[1]) + + # Render fortune template + html_content = """ + + + Fortunes + + + + """ + + for fortune in fortunes: + html_content += f"" + + html_content += """ +
idmessage
{fortune[0]}{fortune[1]}
+ + + """ + + return response.html(html_content) + + +@app.get("/updates") +async def database_updates(request, response): + queries = request.query_params.get("queries") + num_queries = get_num_queries(queries) + # To avoid deadlock + ids = sorted(random.sample(range(1, 10000 + 1), num_queries)) + numbers = sorted(random.sample(range(1, 10000), num_queries)) + updates = list(zip(ids, numbers)) + + worlds = [ + {"id": row_id, "randomNumber": number} for row_id, number in updates + ] + + async with connection_pool.acquire() as connection: + statement = await connection.prepare(READ_ROW_SQL) + for row_id, _ in updates: + await statement.fetchval(row_id) + await connection.executemany(WRITE_ROW_SQL, updates) + + return response.json(worlds) + + +@app.get("/plaintext") +async def plaintext(request, response): + return response.text("Hello, world!") + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8080, workers=multiprocessing.cpu_count()) \ No newline at end of file diff --git a/frameworks/Python/nexios/benchmark_config.json b/frameworks/Python/nexios/benchmark_config.json new file mode 100644 index 00000000000..e30c7e76fae --- /dev/null +++ b/frameworks/Python/nexios/benchmark_config.json @@ -0,0 +1,120 @@ +{ + "framework": "nexios", + "tests": [{ + "default": { + "json_url": "/json", + "fortune_url": "/fortunes", + "plaintext_url": "/plaintext", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "Nexios", + "language": "Python", + "flavor": "Python3", + "orm": "Raw", + "platform": "asyncio", + "webserver": "Uvicorn", + "os": "Linux", + "database_os": "Linux", + "display_name": "Nexios", + "notes": "Nexios is a modern, high-performance ASGI web framework with built-in dependency injection, automatic OpenAPI documentation, and comprehensive middleware support.", + "versus": "fastapi" + }, + "gunicorn": { + "json_url": "/json", + "fortune_url": "/fortunes", + "plaintext_url": "/plaintext", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "Nexios", + "language": "Python", + "flavor": "Python3", + "orm": "Raw", + "platform": "asyncio", + "webserver": "Gunicorn", + "os": "Linux", + "database_os": "Linux", + "display_name": "Nexios-gunicorn", + "notes": "Nexios with Gunicorn server", + "versus": "fastapi" + }, + "uvicorn": { + "json_url": "/json", + "fortune_url": "/fortunes", + "plaintext_url": "/plaintext", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "Nexios", + "language": "Python", + "flavor": "Python3", + "orm": "Raw", + "platform": "asyncio", + "webserver": "Uvicorn", + "os": "Linux", + "database_os": "Linux", + "display_name": "Nexios-uvicorn", + "notes": "Nexios with Uvicorn ASGI server", + "versus": "fastapi" + }, + "granian": { + "json_url": "/json", + "fortune_url": "/fortunes", + "plaintext_url": "/plaintext", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "Nexios", + "language": "Python", + "flavor": "Python3", + "orm": "Raw", + "platform": "asyncio", + "webserver": "Granian", + "os": "Linux", + "database_os": "Linux", + "display_name": "Nexios-granian", + "notes": "Nexios with Granian server (Rust-based ASGI server)", + "versus": "fastapi" + }, + "socketify-asgi": { + "json_url": "/json", + "fortune_url": "/fortunes", + "plaintext_url": "/plaintext", + "db_url": "/db", + "query_url": "/queries?queries=", + "update_url": "/updates?queries=", + "port": 8080, + "approach": "Realistic", + "classification": "Micro", + "database": "Postgres", + "framework": "Nexios", + "language": "Python", + "flavor": "Python3", + "orm": "Raw", + "platform": "asyncio", + "webserver": "Socketify.py", + "os": "Linux", + "database_os": "Linux", + "display_name": "Nexios [Socketify.py ASGI]", + "notes": "Nexios with Socketify ASGI server (C++-based)", + "versus": "fastapi" + } + }] +} \ No newline at end of file diff --git a/frameworks/Python/nexios/config.toml b/frameworks/Python/nexios/config.toml new file mode 100644 index 00000000000..d57da2b5d96 --- /dev/null +++ b/frameworks/Python/nexios/config.toml @@ -0,0 +1,130 @@ +[framework] +name = "nexios" + +[default] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +urls.query = "/queries?queries=" +urls.update = "/updates?queries=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Micro" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "None" +webserver = "Uvicorn" +versus = "fastapi" + +[gunicorn] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +urls.query = "/queries?queries=" +urls.update = "/updates?queries=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Micro" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "None" +webserver = "Gunicorn" +versus = "fastapi" + +[uvicorn] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +urls.query = "/queries?queries=" +urls.update = "/updates?queries=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Micro" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "None" +webserver = "Uvicorn" +versus = "fastapi" + +[granian] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +urls.query = "/queries?queries=" +urls.update = "/updates?queries=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Micro" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "None" +webserver = "Granian" +versus = "fastapi" + +[socketify-asgi] +urls.plaintext = "/plaintext" +urls.json = "/json" +urls.db = "/db" +urls.query = "/queries?queries=" +urls.update = "/updates?queries=" +urls.fortune = "/fortunes" +approach = "Realistic" +classification = "Micro" +database = "Postgres" +database_os = "Linux" +os = "Linux" +orm = "Raw" +platform = "None" +webserver = "Socketify.py" +versus = "fastapi" + +[framework.tags] +tags = [ + "python", + "asgi", + "postgresql", + "orm", + "json", + "html", + "fortunes", + "updates" +] + +[framework.test_implementations] +implementations = [ + { name = "nexios-uvicorn", dockerfile = "nexios-uvicorn.dockerfile", description = "Nexios with Uvicorn server" }, + { name = "nexios-granian", dockerfile = "nexios-granian.dockerfile", description = "Nexios with Granian server (Rust-based ASGI server)" }, + { name = "nexios-socketify", dockerfile = "nexios-socketify-asgi.dockerfile", description = "Nexios with Socketify ASGI server (C++-based)" } +] + +[database] +type = "postgresql" +version = "15" +host = "tfb-database" +port = 5432 +database = "hello_world" +username = "benchmarkdbuser" +password = "benchmarkdbpass" + +[server] +host = "0.0.0.0" +port = 8080 +workers = "auto" + +[benchmark] +tests = [ + "json", + "db", + "queries", + "fortunes", + "updates", + "plaintext" +] \ No newline at end of file diff --git a/frameworks/Python/nexios/nexios-granian.dockerfile b/frameworks/Python/nexios/nexios-granian.dockerfile new file mode 100644 index 00000000000..dc33d5b2599 --- /dev/null +++ b/frameworks/Python/nexios/nexios-granian.dockerfile @@ -0,0 +1,18 @@ +FROM python:3.13 + +WORKDIR /nexios + +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +RUN pip3 install cython==3.0.12 + +COPY requirements.txt requirements-granian.txt ./ + +RUN pip3 install -r requirements.txt -r requirements-granian.txt + +COPY . ./ + +EXPOSE 8080 + +CMD granian --interface asgi --host 0.0.0.0 --port 8080 --workers $(nproc) app:app \ No newline at end of file diff --git a/frameworks/Python/nexios/nexios-gunicorn.dockerfile b/frameworks/Python/nexios/nexios-gunicorn.dockerfile new file mode 100644 index 00000000000..bea30760ca9 --- /dev/null +++ b/frameworks/Python/nexios/nexios-gunicorn.dockerfile @@ -0,0 +1,18 @@ +FROM python:3.13 + +WORKDIR /nexios + +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +RUN pip3 install cython==3.0.12 + +COPY requirements.txt requirements-gunicorn.txt ./ + +RUN pip3 install -r requirements.txt -r requirements-gunicorn.txt + +COPY . ./ + +EXPOSE 8080 + +CMD gunicorn app:app --bind 0.0.0.0:8080 --workers $(nproc) --worker-class uvicorn.workers.UvicornWorker --log-level error --access-logfile - --error-logfile - \ No newline at end of file diff --git a/frameworks/Python/nexios/nexios-socketify-asgi.dockerfile b/frameworks/Python/nexios/nexios-socketify-asgi.dockerfile new file mode 100644 index 00000000000..191886594cc --- /dev/null +++ b/frameworks/Python/nexios/nexios-socketify-asgi.dockerfile @@ -0,0 +1,19 @@ +FROM python:3.13-bullseye + +WORKDIR /nexios + +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +RUN apt-get update; apt-get install libuv1 -y +RUN pip3 install cython==3.0.12 + +COPY requirements-socketify.txt ./ + +RUN pip3 install -r requirements-socketify.txt + +COPY . ./ + +EXPOSE 8080 + +CMD python ./app-socketify-asgi.py \ No newline at end of file diff --git a/frameworks/Python/nexios/nexios-uvicorn.dockerfile b/frameworks/Python/nexios/nexios-uvicorn.dockerfile new file mode 100644 index 00000000000..3fc0fbc586e --- /dev/null +++ b/frameworks/Python/nexios/nexios-uvicorn.dockerfile @@ -0,0 +1,18 @@ +FROM python:3.13 + +WORKDIR /nexios + +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +RUN pip3 install cython==3.0.12 + +COPY requirements.txt requirements-uvicorn.txt ./ + +RUN pip3 install -r requirements.txt -r requirements-uvicorn.txt + +COPY . ./ + +EXPOSE 8080 + +CMD uvicorn app:app --host 0.0.0.0 --port 8080 --workers $(nproc) --log-level error --no-access-log --no-proxy-headers \ No newline at end of file diff --git a/frameworks/Python/nexios/nexios.dockerfile b/frameworks/Python/nexios/nexios.dockerfile new file mode 100644 index 00000000000..3b7b351c9be --- /dev/null +++ b/frameworks/Python/nexios/nexios.dockerfile @@ -0,0 +1,18 @@ +FROM python:3.13 + +WORKDIR /nexios + +RUN python -m venv /opt/venv +ENV PATH="/opt/venv/bin:$PATH" + +RUN pip3 install cython==3.0.12 + +COPY requirements.txt ./ + +RUN pip3 install -r requirements.txt + +COPY . ./ + +EXPOSE 8080 + +CMD uvicorn app:app --host 0.0.0.0 --port 8080 --workers $(nproc) --log-level error --no-access-log --no-proxy-headers \ No newline at end of file diff --git a/frameworks/Python/nexios/requirements-granian.txt b/frameworks/Python/nexios/requirements-granian.txt new file mode 100644 index 00000000000..a6e7f28960e --- /dev/null +++ b/frameworks/Python/nexios/requirements-granian.txt @@ -0,0 +1 @@ +granian==1.5.0 \ No newline at end of file diff --git a/frameworks/Python/nexios/requirements-gunicorn.txt b/frameworks/Python/nexios/requirements-gunicorn.txt new file mode 100644 index 00000000000..81d52c414bb --- /dev/null +++ b/frameworks/Python/nexios/requirements-gunicorn.txt @@ -0,0 +1 @@ +gunicorn==22.0.0 \ No newline at end of file diff --git a/frameworks/Python/nexios/requirements-socketify.txt b/frameworks/Python/nexios/requirements-socketify.txt new file mode 100644 index 00000000000..2a166222092 --- /dev/null +++ b/frameworks/Python/nexios/requirements-socketify.txt @@ -0,0 +1,2 @@ +nexios==2.4.14 +git+https://github.com/cirospaciari/socketify.py.git@main#socketify \ No newline at end of file diff --git a/frameworks/Python/nexios/requirements-uvicorn.txt b/frameworks/Python/nexios/requirements-uvicorn.txt new file mode 100644 index 00000000000..05cd86dd011 --- /dev/null +++ b/frameworks/Python/nexios/requirements-uvicorn.txt @@ -0,0 +1,3 @@ +uvicorn==0.34.0 +uvloop==0.21.0 +httptools==0.6.4 \ No newline at end of file diff --git a/frameworks/Python/nexios/requirements.txt b/frameworks/Python/nexios/requirements.txt new file mode 100644 index 00000000000..aba3ccbca59 --- /dev/null +++ b/frameworks/Python/nexios/requirements.txt @@ -0,0 +1,2 @@ +nexios==2.4.14 +asyncpg==0.30.0 \ No newline at end of file