Browse Source

add Granian to Python frameworks (#7300)

Giovanni Barillari 3 years ago
parent
commit
aa1a10aa00

+ 29 - 0
frameworks/Python/granian/README.md

@@ -0,0 +1,29 @@
+# Granian Benchmark Test
+
+This is the Granian portion of a [benchmarking tests suite](../../) comparing a variety of web development platforms.
+
+The information below is specific to Granian. 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
+
+[Granian](https://github.com/emmett-framework/granian) is an asyncio server for Python applications.
+
+## Test Paths & Source
+
+Granian includes two different implementations:
+
+- ASGI implementation in the [app\_asgi.py](app_asgi.py)
+- RSGI implementation in the [app\_rsgi.py](app_rsgi.py)
+
+Both implementations includes the following tests:
+
+* JSON Serialization: "/json"
+* Plaintext: "/plaintext"
+
+*Replace # with an actual number.*
+
+## Resources
+
+* [Github repository](https://github.com/emmett-framework/granian)

+ 56 - 0
frameworks/Python/granian/app_asgi.py

@@ -0,0 +1,56 @@
+import orjson
+
+JSON_RESPONSE = {
+    'type': 'http.response.start',
+    'status': 200,
+    'headers': [
+        [b'content-type', b'application/json'],
+    ]
+}
+PLAINTEXT_RESPONSE = {
+    'type': 'http.response.start',
+    'status': 200,
+    'headers': [
+        [b'content-type', b'text/plain; charset=utf-8'],
+    ]
+}
+
+json_dumps = orjson.dumps
+
+
+async def route_json(scope, receive, send):
+    await send(JSON_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': json_dumps({'message': 'Hello, world!'}),
+        'more_body': False
+    })
+
+
+async def route_plaintext(scope, receive, send):
+    await send(PLAINTEXT_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': b'Hello, world!',
+        'more_body': False
+    })
+
+
+async def handle_404(scope, receive, send):
+    await send(PLAINTEXT_RESPONSE)
+    await send({
+        'type': 'http.response.body',
+        'body': b'Not found',
+        'more_body': False
+    })
+
+
+routes = {
+    '/json': route_json,
+    '/plaintext': route_plaintext
+}
+
+
+def main(scope, receive, send):
+    handler = routes.get(scope['path'], handle_404)
+    return handler(scope, receive, send)

+ 40 - 0
frameworks/Python/granian/app_rsgi.py

@@ -0,0 +1,40 @@
+import orjson
+
+from granian.rsgi import Response
+
+JSON_HEADERS = {'content-type': 'application/json'}
+PLAINTEXT_HEADERS = {'content-type': 'text/plain; charset=utf-8'}
+
+json_dumps = orjson.dumps
+
+
+async def route_json(scope, receive):
+    return Response(
+        1, 200, JSON_HEADERS,
+        json_dumps({'message': 'Hello, world!'}), None, None
+    )
+
+
+async def route_plaintext(scope, receive):
+    return Response(
+        2, 200, PLAINTEXT_HEADERS,
+        None, 'Hello, world!', None
+    )
+
+
+async def handle_404(scope, receive):
+    return Response(
+        2, 404, PLAINTEXT_HEADERS,
+        None, 'Not found', None
+    )
+
+
+routes = {
+    '/json': route_json,
+    '/plaintext': route_plaintext
+}
+
+
+def main(scope, receive):
+    handler = routes.get(scope.path, handle_404)
+    return handler(scope, receive)

+ 41 - 0
frameworks/Python/granian/benchmark_config.json

@@ -0,0 +1,41 @@
+{
+  "framework": "granian",
+  "tests": [{
+    "default": {
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "Postgres",
+      "framework": "granian",
+      "language": "Python",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "granian",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "granian [asgi]",
+      "notes": "",
+      "versus": "uvicorn"
+    },
+    "rsgi": {
+      "json_url": "/json",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "Postgres",
+      "framework": "granian",
+      "language": "Rust",
+      "orm": "Raw",
+      "platform": "None",
+      "webserver": "granian",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "granian [rsgi]",
+      "notes": "",
+      "versus": "uvicorn"
+    }
+  }]
+}

+ 28 - 0
frameworks/Python/granian/config.toml

@@ -0,0 +1,28 @@
+[framework]
+name = "granian"
+
+[main]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Platform"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "None"
+webserver = "granian"
+versus = "uvicorn"
+
+[rsgi]
+urls.plaintext = "/plaintext"
+urls.json = "/json"
+approach = "Realistic"
+classification = "Platform"
+database = "Postgres"
+database_os = "Linux"
+os = "Linux"
+orm = "Raw"
+platform = "None"
+webserver = "granian"
+versus = "uvicorn"

+ 11 - 0
frameworks/Python/granian/granian-rsgi.dockerfile

@@ -0,0 +1,11 @@
+FROM python:3.8
+
+ADD ./ /granian
+
+WORKDIR /granian
+
+RUN pip install -r /granian/requirements.txt
+
+EXPOSE 8080
+
+CMD python run.py rsgi

+ 11 - 0
frameworks/Python/granian/granian.dockerfile

@@ -0,0 +1,11 @@
+FROM python:3.8
+
+ADD ./ /granian
+
+WORKDIR /granian
+
+RUN pip install -r /granian/requirements.txt
+
+EXPOSE 8080
+
+CMD python run.py asgi

+ 3 - 0
frameworks/Python/granian/requirements.txt

@@ -0,0 +1,3 @@
+granian==0.1.0a1
+orjson==3.6.8
+uvloop==0.16.0

+ 17 - 0
frameworks/Python/granian/run.py

@@ -0,0 +1,17 @@
+import multiprocessing
+import sys
+
+from granian import Granian
+
+
+if __name__ == '__main__':
+    interface = sys.argv[1]
+
+    Granian(
+        f"app_{interface}:main",
+        address="0.0.0.0",
+        port=8080,
+        workers=multiprocessing.cpu_count(),
+        backlog=2048,
+        interface=interface
+    ).serve()

+ 10 - 0
frameworks/Python/granian/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>