浏览代码

feat: Updates the Starlite application test cases. (#7908)

* updated starlite

* fix: reference global connection_pool object

* fix: explicitly set loop to use uvloop

---------

Co-authored-by: Na'aman Hirschfeld <[email protected]>
Co-authored-by: Cody Fincher <[email protected]>
Cody Fincher 2 年之前
父节点
当前提交
08d672ed0b

+ 4 - 3
.gitignore

@@ -1,6 +1,4 @@
-.DS_Store
 installs
-*.log
 node_modules/
 .travis.bak
 
@@ -64,4 +62,7 @@ benchmark.cfg
 .*.sw[a-p]
 
 # merge tooling
-*.orig
+*.orig
+
+# python
+.venv/

+ 2 - 0
frameworks/Python/starlite/.dockerignore

@@ -0,0 +1,2 @@
+.venv
+README.md

+ 4 - 25
frameworks/Python/starlite/README.md

@@ -10,29 +10,8 @@ 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).
+Starlite is a powerful, performant, flexible and opinionated ASGI framework,
+offering first class typing support and a full [Pydantic](https://github.com/samuelcolvin/pydantic)
+integration.
 
-## 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)
+Check out the [documentation 📚](https://starlite-api.github.io/starlite/).

+ 49 - 53
frameworks/Python/starlite/app.py

@@ -1,24 +1,21 @@
 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
+from random import randint, sample
+from typing import Any
 
-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.']
+import uvloop
+from asyncpg import create_pool
+from jinja2 import Template
+from starlite import MediaType, Starlite, get
 
-connection_pool: Pool
+asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
+connection_pool: Any = None
 
 
-async def setup_database():
+async def init_connection_pool() -> None:
     global connection_pool
-    connection_pool = await asyncpg.create_pool(
+    connection_pool = await create_pool(
         user=os.getenv('PGUSER', 'benchmarkdbuser'),
         password=os.getenv('PGPASS', 'benchmarkdbpass'),
         database='hello_world',
@@ -27,54 +24,49 @@ async def setup_database():
     )
 
 
-def load_fortunes_template():
+def normalize_queries(value: str | None) -> int:
+    queries = int(value) if value and value.isnumeric() else 1
+    if queries > 500:
+        return 500
+    if queries < 1:
+        return 1
+    return queries
+
+
+def load_fortunes_template() -> "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)
+        return 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()
+fortune_template = load_fortunes_template()
 
 
 @get(path='/json')
-async def json_serialization() -> dict[str, str]:
+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)
+        number = await connection.fetchval(
+            'SELECT "randomnumber", "id" FROM "world" WHERE id = $1',
+            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)]
+async def multiple_database_queries(queries: None | str = None) -> list[dict[str, int]]:
+    row_ids = sample(range(1, 10000), normalize_queries(queries))
     worlds = []
 
     async with connection_pool.acquire() as connection:
-        statement = await connection.prepare(READ_ROW_SQL)
+        statement = await connection.prepare('SELECT "randomnumber", "id" FROM "world" WHERE id = $1')
         for row_id in row_ids:
             number = await statement.fetchval(row_id)
             worlds.append({'id': row_id, 'randomNumber': number})
@@ -87,39 +79,43 @@ 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)
+    fortunes.append([0, 'Additional fortune added at request time.'])
+    fortunes.sort(key=itemgetter(1))
+    return fortune_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 def database_updates(queries: None | str = None) -> list[dict[str, int]]:
+    num_queries = normalize_queries(queries)
+    updates = list(zip(sorted(sample(range(1, 10000 + 1), num_queries)), sample(range(1, 10000), 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:
+        statement = await connection.prepare('SELECT "id", "randomnumber" FROM "world" WHERE id = $1')
+        for row_id, _ in updates:
             await statement.fetchval(row_id)
-        await connection.executemany(WRITE_ROW_SQL, updates)
+        await connection.executemany('UPDATE "world" SET "randomnumber"=$1 WHERE id=$2', updates)
 
     return worlds
 
 
 @get(path='/plaintext', media_type=MediaType.TEXT)
-async def plaintext() -> bytes:
+def plaintext() -> bytes:
     return b'Hello, world!'
 
 
 app = Starlite(
     route_handlers=[
+        database_updates,
         json_serialization,
-        single_database_query,
         multiple_database_queries,
+        plaintext,
         render_fortunes_template,
-        database_updates,
-        plaintext
+        single_database_query,
     ],
-    on_startup=[setup_database]
+    on_startup=[init_connection_pool],
+    openapi_config=None,
 )

+ 1 - 1
frameworks/Python/starlite/benchmark_config.json

@@ -18,7 +18,7 @@
         "flavor": "Python3",
         "orm": "Raw",
         "platform": "None",
-        "webserver": "None",
+        "webserver": "Uvicorn",
         "os": "Linux",
         "database_os": "Linux",
         "display_name": "Starlite",

+ 3 - 3
frameworks/Python/starlite/config.toml

@@ -1,7 +1,7 @@
 [framework]
 name = "starlite"
 
-[main]
+[uvicorn]
 urls.plaintext = "/plaintext"
 urls.json = "/json"
 urls.db = "/db"
@@ -15,5 +15,5 @@ database_os = "Linux"
 os = "Linux"
 orm = "Raw"
 platform = "None"
-webserver = "None"
-versus = "None"
+webserver = "Uvicorn"
+versus = "None"

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

@@ -1,6 +1,5 @@
-asyncpg>=0.26.0
-Jinja2>=3.1.2
-uvicorn>=0.18.2
-starlite>=1.7.1
-gunicorn>=20.1.0
-uvloop>=0.16.0
+asyncpg>=0.27.0
+jinja2>=3.1.2
+starlite>=1.51.0
+uvicorn[standard]>=0.20.0
+uvloop>=0.17.0

+ 11 - 5
frameworks/Python/starlite/starlite.dockerfile

@@ -1,8 +1,14 @@
-FROM python:3.10
-EXPOSE 8080
-WORKDIR /starlite
+FROM python:3.11
+WORKDIR /starlite/
+
+RUN python -m venv /opt/venv
+ENV PATH="/opt/venv/bin:$PATH"
+
 COPY . .
+
 RUN pip install --upgrade pip \
-    && pip install cython==0.29.26 \
+    && pip install cython==0.29.33 \
     && pip install -r /starlite/requirements.txt
-CMD gunicorn app:app -k uvicorn.workers.UvicornWorker -c starlite_conf.py
+
+EXPOSE 8080
+CMD uvicorn app:app --host 0.0.0.0 --port 8080 --workers $(nproc) --log-level error --loop uvloop

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

@@ -1,14 +0,0 @@
-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 - 3
frameworks/Python/starlite/templates/fortune.html

@@ -3,8 +3,15 @@
 <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>
+    <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>