Selaa lähdekoodia

added starlite (#7017)

* added starlite

* updated starlite to latest version

* updated starlite to latest version

* updated starlite
Na'aman Hirschfeld 3 vuotta sitten
vanhempi
commit
9b9e0951a8

+ 38 - 0
frameworks/Python/starlite/README.md

@@ -0,0 +1,38 @@
+# Starlite Benchmarking Test
+
+This is the Starlite portion of a [benchmarking tests suite](../../)
+comparing a variety of web development platforms.
+
+The information below is specific to Starlite. For further guidance,
+review the [documentation](https://github.com/TechEmpower/FrameworkBenchmarks/wiki).
+Also note that there is additional information provided in
+the [Python README](../).
+
+## Description# Starlite
+
+Starlite is a light, opinionated and flexible ASGI API framework built on top
+of [pydantic](https://github.com/samuelcolvin/pydantic) and [Starlette](https://github.com/encode/starlette).
+
+## Core Features
+
+* 👉 Class based controllers
+* 👉 Decorators based configuration
+* 👉 Extended testing support
+* 👉 Extensive typing support including inference, validation and parsing
+* 👉 Full async (ASGI) support
+* 👉 Layered dependency injection
+* 👉 OpenAPI 3.1 schema generation with [Redoc](https://github.com/Redocly/redoc) UI
+* 👉 Route guards based authorization
+* 👉 Simple middleware and authentication
+* 👉 Support for pydantic models and pydantic dataclasses
+* 👉 Support for standard library dataclasses
+* 👉 Ultra-fast json serialization and deserialization using [orjson](https://github.com/ijl/orjson)
+
+## Test Paths & Sources
+
+The API is implemented in a single file ([app.py](app.py)). This Test is based on the Starlette tests.
+
+## Resources
+
+* [Starlite Documentation 📚](https://starlite-api.github.io/starlite/)
+* [Starlite Repository ](https://github.com/starlite-api/starlite)

+ 125 - 0
frameworks/Python/starlite/app.py

@@ -0,0 +1,125 @@
+import asyncio
+from typing import Optional, Any
+
+import asyncpg
+import os
+import jinja2
+from asyncpg import Pool
+from starlite import Starlite, get, MediaType
+from random import randint
+from operator import itemgetter
+
+READ_ROW_SQL = 'SELECT "randomnumber", "id" 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.']
+
+connection_pool: Pool
+
+
+async def setup_database():
+    global connection_pool
+    connection_pool = await asyncpg.create_pool(
+        user=os.getenv('PGUSER', 'benchmarkdbuser'),
+        password=os.getenv('PGPASS', 'benchmarkdbpass'),
+        database='hello_world',
+        host='tfb-database',
+        port=5432
+    )
+
+
+def load_fortunes_template():
+    path = os.path.join('templates', 'fortune.html')
+    with open(path, 'r') as template_file:
+        template_text = template_file.read()
+        return jinja2.Template(template_text)
+
+
+def get_num_queries(queries: Any):
+    if 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
+    return 1
+
+
+sort_fortunes_key = itemgetter(1)
+template = load_fortunes_template()
+
+
+@get(path='/json')
+async def json_serialization() -> dict[str, str]:
+    return {'message': 'Hello, world!'}
+
+
+@get(path='/db')
+async def single_database_query() -> dict[str, int]:
+    row_id = randint(1, 10000)
+
+    async with connection_pool.acquire() as connection:
+        number = await connection.fetchval(READ_ROW_SQL, row_id)
+
+    return {'id': row_id, 'randomNumber': number}
+
+
+@get(path='/queries')
+async def multiple_database_queries(queries: Any = None) -> list[dict[str, int]]:
+    num_queries = get_num_queries(queries)
+    row_ids = [randint(1, 10000) for _ in range(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 worlds
+
+
+@get(path='/fortunes', media_type=MediaType.HTML)
+async def render_fortunes_template() -> str:
+    async with connection_pool.acquire() as connection:
+        fortunes = await connection.fetch('SELECT * FROM Fortune')
+
+    fortunes.append(ADDITIONAL_ROW)
+    fortunes.sort(key=sort_fortunes_key)
+    return template.render(fortunes=fortunes)
+
+
+@get(path='/updates')
+async def database_updates(queries: Any = None) -> list[dict[str, int]]:
+    num_queries = get_num_queries(queries)
+    updates = [(randint(1, 10000), randint(1, 10000)) for _ in range(num_queries)]
+    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, number in updates:
+            await statement.fetchval(row_id)
+        await connection.executemany(WRITE_ROW_SQL, updates)
+
+    return worlds
+
+
+@get(path='/plaintext', media_type=MediaType.TEXT)
+async def plaintext() -> bytes:
+    return b'Hello, world!'
+
+
+app = Starlite(
+    route_handlers=[
+        json_serialization,
+        single_database_query,
+        multiple_database_queries,
+        render_fortunes_template,
+        database_updates,
+        plaintext
+    ],
+    on_startup=[setup_database]
+)

+ 30 - 0
frameworks/Python/starlite/benchmark_config.json

@@ -0,0 +1,30 @@
+{
+  "framework": "starlite",
+  "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": "Starlite",
+        "language": "Python",
+        "flavor": "Python3",
+        "orm": "Raw",
+        "platform": "None",
+        "webserver": "None",
+        "os": "Linux",
+        "database_os": "Linux",
+        "display_name": "Starlite",
+        "notes": "",
+        "versus": "None"
+      }
+    }
+  ]
+}

+ 19 - 0
frameworks/Python/starlite/config.toml

@@ -0,0 +1,19 @@
+[framework]
+name = "starlite"
+
+[main]
+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 = "None"
+versus = "None"

+ 5 - 0
frameworks/Python/starlite/requirements.txt

@@ -0,0 +1,5 @@
+asyncpg==0.25.0
+Jinja2==3.0.3
+uvicorn==0.17.1
+starlite==1.0.0
+gunicorn==20.1.0

+ 8 - 0
frameworks/Python/starlite/starlite.dockerfile

@@ -0,0 +1,8 @@
+FROM python:3.10
+EXPOSE 8080
+WORKDIR /starlite
+COPY . .
+RUN pip install --upgrade pip \
+    && pip install cython==0.29.26 \
+    && pip install -r /starlite/requirements.txt
+CMD gunicorn app:app -k uvicorn.workers.UvicornWorker -c starlite_conf.py

+ 14 - 0
frameworks/Python/starlite/starlite_conf.py

@@ -0,0 +1,14 @@
+import multiprocessing
+import os
+
+_is_travis = os.environ.get('TRAVIS') == 'true'
+
+workers = multiprocessing.cpu_count()
+if _is_travis:
+    workers = 2
+
+bind = "0.0.0.0:8080"
+keepalive = 120
+errorlog = '-'
+pidfile = '/tmp/starlite.pid'
+loglevel = 'error'

+ 10 - 0
frameworks/Python/starlite/templates/fortune.html

@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head><title>Fortunes</title></head>
+<body>
+<table>
+<tr><th>id</th><th>message</th></tr>
+{% for fortune in fortunes %}<tr><td>{{ fortune[0] }}</td><td>{{ fortune[1]|e }}</td></tr>
+{% endfor %}</table>
+</body>
+</html>