Browse Source

Added Emmett to Python frameworks (#5404)

* Updated weppy framework to 1.0

* Updated weppy to 1.0.1

* [Python] Added Emmett framework

* Updated travis config

* Updated docker deps

* Fixed uvicorn config

* Updated emmett to 2.0.0a2

* Updated Emmett to 2.0.0a3
Giovanni Barillari 5 years ago
parent
commit
8ae345c888

+ 1 - 1
.travis.yml

@@ -63,7 +63,7 @@ env:
     - 'TESTDIR="PHP/cakephp PHP/codeigniter PHP/fat-free PHP/fuel PHP/kumbiaphp PHP/phpixie PHP/slim PHP/symfony PHP/yii2 PHP/zend PHP/spiral"'
     - 'TESTDIR="PHP/cakephp PHP/codeigniter PHP/fat-free PHP/fuel PHP/kumbiaphp PHP/phpixie PHP/slim PHP/symfony PHP/yii2 PHP/zend PHP/spiral"'
     - 'TESTDIR="PHP/amp PHP/hhvm PHP/peachpie PHP/php-ngx PHP/workerman PHP/phalcon"'
     - 'TESTDIR="PHP/amp PHP/hhvm PHP/peachpie PHP/php-ngx PHP/workerman PHP/phalcon"'
     - 'TESTDIR="PHP/hamlet PHP/laravel PHP/lumen PHP/swoole PHP/ubiquity PHP/hyperf PHP/sw-fw-less PHP/imi"'
     - 'TESTDIR="PHP/hamlet PHP/laravel PHP/lumen PHP/swoole PHP/ubiquity PHP/hyperf PHP/sw-fw-less PHP/imi"'
-    - 'TESTDIR="Python/aiohttp Python/api_hour Python/apidaora Python/blacksheep Python/bottle Python/cherrypy Python/django Python/eve Python/falcon Python/fastapi Python/flask"'
+    - 'TESTDIR="Python/aiohttp Python/api_hour Python/apidaora Python/blacksheep Python/bottle Python/cherrypy Python/django Python/emmett Python/eve Python/falcon Python/fastapi Python/flask"'
     - 'TESTDIR="Python/hug Python/japronto Python/klein Python/morepath Python/pyramid Python/quart Python/responder Python/sanic Python/spyne Python/starlette"'
     - 'TESTDIR="Python/hug Python/japronto Python/klein Python/morepath Python/pyramid Python/quart Python/responder Python/sanic Python/spyne Python/starlette"'
     - 'TESTDIR="Python/tornado Python/turbogears Python/uvicorn Python/uwsgi Python/vibora Python/web2py Python/webware Python/weppy Python/wsgi"'
     - 'TESTDIR="Python/tornado Python/turbogears Python/uvicorn Python/uwsgi Python/vibora Python/web2py Python/webware Python/weppy Python/wsgi"'
     - 'TESTDIR="Ruby/agoo Ruby/grape Ruby/h2o_mruby Ruby/hanami Ruby/padrino Ruby/rack Ruby/rack-sequel"'
     - 'TESTDIR="Ruby/agoo Ruby/grape Ruby/h2o_mruby Ruby/hanami Ruby/padrino Ruby/rack Ruby/rack-sequel"'

+ 20 - 0
frameworks/Python/emmett/README.md

@@ -0,0 +1,20 @@
+# Emmett Benchmark Test
+
+[Emmett](https://github.com/emmett-framework/emmett) is a fullstack Python asyncIO web framework.
+
+This test uses the included ORM and templating engine, and uvicorn for the application server on CPtyhon.
+
+## Test Paths & Source
+
+* [JSON Serialization](app.py): "/json"
+* [Single Database Query](app.py): "/db"
+* [Multiple Database Queries](app.py): "queries?queries=#"
+* [Fortunes](app.py): "/fortunes"
+* [Database Updates](app.py): "updates?queries=#"
+* [Plaintext](app.py): "/plaintext"
+
+*Replace # with an actual number.*
+
+### Resources
+
+* [Github repository](https://github.com/emmett-framework/emmett)

+ 102 - 0
frameworks/Python/emmett/app.py

@@ -0,0 +1,102 @@
+
+from functools import partial
+from random import randint
+
+from emmett import App, request, response
+from emmett.orm import Database, Model, Field, rowmethod
+from emmett.tools import service
+
+app = App(__name__)
+
+
+class World(Model):
+    tablename = "world"
+    randomnumber = Field.int()
+
+    @rowmethod('serialize')
+    def _serialize(self, row):
+        return {'id': row.id, 'randomNumber': row.randomnumber}
+
+
+class Fortune(Model):
+    tablename = "fortune"
+    message = Field.string()
+
+    @rowmethod('serialize')
+    def _serialize(self, row):
+        return {'id': row.id, 'message': row.message}
+
+
+app.config.handle_static = False
+app.config.db.adapter = 'postgres:psycopg2'
+app.config.db.host = 'tfb-database'
+app.config.db.user = 'benchmarkdbuser'
+app.config.db.password = 'benchmarkdbpass'
+app.config.db.database = 'hello_world'
+app.config.db.pool_size = 10
+
+db = Database(app)
+db.define_models(World, Fortune)
+
+
[email protected]()
[email protected]
+async def json():
+    return {'message': 'Hello, World!'}
+
+
[email protected]("/db", pipeline=[db.pipe])
[email protected]
+async def get_random_world():
+    return World.get(randint(1, 10000)).serialize()
+
+
+def get_qparam():
+    try:
+        rv = int(request.query_params.queries or 1)
+    except ValueError:
+        return 1
+    if rv < 1:
+        return 1
+    if rv > 500:
+        return 500
+    return rv
+
+
[email protected]("/queries", pipeline=[db.pipe])
[email protected]
+async def get_random_worlds():
+    num_queries = get_qparam()
+    worlds = [
+        World.get(randint(1, 10000)).serialize() for _ in range(num_queries)]
+    return worlds
+
+
[email protected](pipeline=[db.pipe])
+async def fortunes():
+    fortunes = Fortune.all().select()
+    fortunes.append(
+        Fortune.new(id=0, message="Additional fortune added at request time."))
+    fortunes.sort(lambda m: m.message)
+    return {'fortunes': fortunes}
+
+
[email protected](pipeline=[db.pipe])
[email protected]
+async def updates():
+    num_queries = get_qparam()
+    worlds = []
+    rp = partial(randint, 1, 10000)
+    ids = [rp() for _ in range(num_queries)]
+    ids.sort()  # To avoid deadlock
+    for id in ids:
+        world = World.get(id)
+        world.update_record(randomnumber=rp())
+        worlds.append(world.serialize())
+    return worlds
+
+
[email protected](output='bytes')
+async def plaintext():
+    response.headers["Content-Type"] = "text/plain"
+    return b'Hello, World!'

+ 27 - 0
frameworks/Python/emmett/benchmark_config.json

@@ -0,0 +1,27 @@
+{
+  "framework": "emmett",
+  "tests": [{
+    "default": {
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/updates?queries=",
+      "plaintext_url": "/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "Postgres",
+      "framework": "Emmett",
+      "language": "Python",
+      "orm": "Full",
+      "platform": "None",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Emmett",
+      "notes": "CPython 3.7",
+      "versus": "uvicorn"
+    }
+  }]
+}

+ 18 - 0
frameworks/Python/emmett/emmett.dockerfile

@@ -0,0 +1,18 @@
+FROM python:3.7-alpine
+
+RUN apk add --no-cache libpq libstdc++
+
+RUN mkdir -p /usr/src/app
+WORKDIR /usr/src/app
+COPY requirements.txt /usr/src/app
+RUN apk add --no-cache --virtual build-deps \
+    g++ libffi-dev libuv-dev make musl-dev openssl-dev postgresql-dev && \
+    pip install --no-cache-dir -r /usr/src/app/requirements.txt && \
+    apk del build-deps
+
+COPY ./ /app
+WORKDIR /app
+
+EXPOSE 8080
+
+CMD [ "gunicorn", "app:app" , "-k", "emmett.asgi.workers.EmmettWorker", "-c", "gunicorn_conf.py" ]

+ 13 - 0
frameworks/Python/emmett/gunicorn_conf.py

@@ -0,0 +1,13 @@
+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 = '-'
+loglevel = 'error'

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

@@ -0,0 +1,3 @@
+emmett==2.0.0a3
+psycopg2==2.8.4
+gunicorn==19.9.0

+ 20 - 0
frameworks/Python/emmett/templates/fortunes.html

@@ -0,0 +1,20 @@
+<!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.id }}</td>
+                <td>{{ =fortune.message }}</td>
+            </tr>
+            {{ pass }}
+        </table>
+    </body>
+</html>