Prechádzať zdrojové kódy

Upgrade to Microdot 2 (#8663)

Miguel Grinberg 1 rok pred
rodič
commit
90f6e3d6b0

+ 7 - 14
frameworks/Python/microdot/README.md

@@ -8,20 +8,13 @@ Also note that there is additional information provided in the [Python README](.
 
 ### Test Source Code
 
-* [JSON](app_sync.py#L60)
-* [JSON-async](app_async.py#L60)
-* [PLAINTEXT](app_sync.py#L102)
-* [PLAINTEXT-async](app_async.py#L102)
-* [DB](app_sync.py#L65)
-* [DB-async](app_async.py#L65)
-* [QUERY](app_sync.py#L73)
-* [QUERY-async](app_async.py#L83)
-* [CACHED QUERY](app_sync.py#L112)
-* [CACHED_QUERY-async](app_async.py#L112)
-* [UPDATE](app_sync.py#L89)
-* [UPDATE-async](app_async.py#L89)
-* [FORTUNES](app_sync.py#L80)
-* [FORTUNES-async](app_async.py#L80)
+* [JSON](app.py#L60)
+* [PLAINTEXT](app.py#L102)
+* [DB](app.py#L65)
+* [QUERY](app.py#L73)
+* [CACHED QUERY](app.py#L112)
+* [UPDATE](app.py#L89)
+* [FORTUNES](app.py#L80)
 
 ## Resources
 

+ 19 - 16
frameworks/Python/microdot/app_async.py → frameworks/Python/microdot/app.py

@@ -1,42 +1,42 @@
 #!/usr/bin/env python
-from datetime import datetime
 import os
 from random import randint, sample
 
-from alchemical.aio import Alchemical
-import sqlalchemy as sqla
+from alchemical.aio import Alchemical, Model
+import sqlalchemy.orm as so
 from asyncache import cached
 from cachetools.keys import hashkey
 
-from microdot_asgi import Microdot
-from microdot_jinja import render_template
+from microdot.asgi import Microdot
+from microdot.jinja import Template
 
 app = Microdot()
-db = Alchemical(os.environ['DATABASE_URL'])
+Template.initialize('templates', enable_async=True)
+db = Alchemical(os.environ.get('DATABASE_URL', 'sqlite:///'))
 
 
-class World(db.Model):
+class World(Model):
     __tablename__ = "world"
-    id = sqla.Column(sqla.Integer, primary_key=True)
-    randomnumber = sqla.Column(sqla.Integer)
+    id: so.Mapped[int] = so.mapped_column(primary_key=True)
+    randomnumber: so.Mapped[int]
 
     def to_dict(self):
         return {"id": self.id, "randomNumber": self.randomnumber}
 
 
-class CachedWorld(db.Model):
+class CachedWorld(Model):
     __tablename__ = "cachedworld"
-    id = sqla.Column(sqla.Integer, primary_key=True)
-    randomnumber = sqla.Column(sqla.Integer)
+    id: so.Mapped[int] = so.mapped_column(primary_key=True)
+    randomnumber: so.Mapped[int]
 
     def to_dict(self):
         return {"id": self.id, "randomNumber": self.randomnumber}
 
 
-class Fortune(db.Model):
+class Fortune(Model):
     __tablename__ = "fortune"
-    id = sqla.Column(sqla.Integer, primary_key=True)
-    message = sqla.Column(sqla.String)
+    id: so.Mapped[int] = so.mapped_column(primary_key=True)
+    message: so.Mapped[str]
 
 
 def get_num_queries(request, name="queries"):
@@ -81,7 +81,10 @@ async def test_fortunes(request):
         fortunes = list(await session.scalars(Fortune.select()))
     fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
     fortunes.sort(key=lambda f: f.message)
-    return render_template("fortunes.html", fortunes=fortunes), {'Content-Type': 'text/html; charset=utf-8'}
+    return (
+        await Template("fortunes.html").render_async(fortunes=fortunes),
+        {'Content-Type': 'text/html; charset=utf-8'},
+    )
 
 
 @app.route("/updates")

+ 9 - 4
frameworks/Python/microdot/app_async_raw.py → frameworks/Python/microdot/app_raw.py

@@ -1,5 +1,4 @@
 #!/usr/bin/env python
-from datetime import datetime
 import os
 from random import randint, sample
 
@@ -7,15 +6,18 @@ import asyncpg
 from asyncache import cached
 from cachetools.keys import hashkey
 
-from microdot_asgi import Microdot
-from microdot_jinja import render_template
+from microdot.asgi import Microdot
+from microdot.jinja import Template
 
 app = Microdot()
+Template.initialize('templates', enable_async=True)
+
 get_world_sql = 'SELECT id, randomnumber FROM world WHERE id = $1'
 update_world_sql = 'UPDATE world SET randomnumber = $1 WHERE id = $2'
 fortune_sql = 'SELECT * FROM fortune'
 db = None
 
+
 async def asgi(scope, receive, send):
     if scope['type'] == 'lifespan':
         while True:
@@ -81,7 +83,10 @@ async def test_fortunes(request):
         fortunes = list(await conn.fetch(fortune_sql))
     fortunes.append((0, "Additional fortune added at request time."))
     fortunes.sort(key=lambda f: f[1])
-    return render_template("fortunes_raw.html", fortunes=fortunes), {'Content-Type': 'text/html; charset=utf-8'}
+    return (
+        await Template("fortunes_raw.html").render_async(fortunes=fortunes),
+        {'Content-Type': 'text/html; charset=utf-8'},
+    )
 
 
 @app.route("/updates")

+ 0 - 115
frameworks/Python/microdot/app_sync.py

@@ -1,115 +0,0 @@
-#!/usr/bin/env python
-from datetime import datetime
-from functools import lru_cache
-import os
-from random import randint, sample
-
-from alchemical import Alchemical
-import sqlalchemy as sqla
-from cachetools import cached
-from cachetools.keys import hashkey
-
-from microdot_wsgi import Microdot
-from microdot_jinja import render_template
-
-app = Microdot()
-db = Alchemical(os.environ['DATABASE_URL'])
-
-
-class World(db.Model):
-    __tablename__ = "world"
-    id = sqla.Column(sqla.Integer, primary_key=True)
-    randomnumber = sqla.Column(sqla.Integer)
-
-    def to_dict(self):
-        return {"id": self.id, "randomNumber": self.randomnumber}
-
-
-class CachedWorld(db.Model):
-    __tablename__ = "cachedworld"
-    id = sqla.Column(sqla.Integer, primary_key=True)
-    randomnumber = sqla.Column(sqla.Integer)
-
-    def to_dict(self):
-        return {"id": self.id, "randomNumber": self.randomnumber}
-
-
-class Fortune(db.Model):
-    __tablename__ = "fortune"
-    id = sqla.Column(sqla.Integer, primary_key=True)
-    message = sqla.Column(sqla.String)
-
-
-def get_num_queries(request, name="queries"):
-    try:
-        num_queries = request.args.get(name, 1, type=int)
-    except ValueError:
-        num_queries = 1
-    if num_queries < 1:
-        return 1
-    if num_queries > 500:
-        return 500
-    return num_queries
-
-
-def generate_ids(num_queries):
-    return sample(range(1, 10001), num_queries)
-
-
[email protected]("/json")
-def test_json(request):
-    return {"message": "Hello, World!"}
-
-
[email protected]("/db")
-def test_db(request):
-    id = randint(1, 10000)
-    with db.Session() as session:
-        world = session.get(World, id)
-    return world.to_dict()
-
-
[email protected]("/queries")
-def test_queries(request):
-    with db.Session() as session:
-        worlds = [session.get(World, id).to_dict() for id in generate_ids(get_num_queries(request))]
-    return worlds
-
-
[email protected]("/fortunes")
-def test_fortunes(request):
-    with db.Session() as session:
-        fortunes = list(session.scalars(Fortune.select()))
-    fortunes.append(Fortune(id=0, message="Additional fortune added at request time."))
-    fortunes.sort(key=lambda f: f.message)
-    return render_template("fortunes.html", fortunes=fortunes), {'Content-Type': 'text/html; charset=utf-8'}
-
-
[email protected]("/updates")
-def test_updates(request):
-    worlds = []
-    ids = generate_ids(get_num_queries(request))
-    ids.sort()  # to avoid deadlocks
-    with db.begin() as session:
-        for id in ids:
-            world = session.get(World, id)
-            world.randomnumber = (randint(1, 9999) + world.randomnumber - 1) % 10000 + 1
-            worlds.append(world.to_dict())
-    return worlds
-
-
[email protected]("/plaintext")
-def test_plaintext(request):
-    return b"Hello, World!"
-
-
-@cached(cache={}, key=lambda session, id: hashkey(id))
-def get_cached_world(session, id):
-    return session.get(World, id).to_dict()
-
-
[email protected]("/cached-queries")
-def test_cached_queries(request):
-    with db.Session() as session:
-        worlds = [get_cached_world(session, id) for id in generate_ids(get_num_queries(request, 'count'))]
-    return worlds

+ 0 - 112
frameworks/Python/microdot/app_sync_raw.py

@@ -1,112 +0,0 @@
-#!/usr/bin/env python
-from datetime import datetime
-from functools import lru_cache
-import os
-from random import randint, sample
-
-from microdot_wsgi import Microdot
-from microdot_jinja import render_template
-import psycopg2
-from psycopg2.extras import execute_batch
-from cachetools import cached
-from cachetools.keys import hashkey
-
-app = Microdot()
-db = psycopg2.connect(os.environ['DATABASE_URL'])
-
-get_world_sql = 'SELECT id, randomnumber FROM world WHERE id = $1'
-update_world_sql = 'UPDATE world SET randomnumber = $1 WHERE id = $2'
-fortune_sql = 'SELECT * FROM fortune'
-with db.cursor() as cur:
-    cur.execute('PREPARE get_world AS ' + get_world_sql)
-    cur.execute('PREPARE update_world AS ' + update_world_sql)
-    cur.execute('PREPARE fortune AS ' + fortune_sql)
-
-
-def get_num_queries(request, name="queries"):
-    try:
-        num_queries = request.args.get(name, 1, type=int)
-    except ValueError:
-        num_queries = 1
-    if num_queries < 1:
-        return 1
-    if num_queries > 500:
-        return 500
-    return num_queries
-
-
-def generate_ids(num_queries):
-    return sample(range(1, 10001), num_queries)
-
-
[email protected]("/json")
-def test_json(request):
-    return {"message": "Hello, World!"}
-
-
[email protected]("/db")
-def test_db(request):
-    id = randint(1, 10000)
-    with db.cursor() as cur:
-        cur.execute('EXECUTE get_world (%s)', (id,))
-        result = cur.fetchone()
-        world = {'id': result[0], 'randomNumber': result[1]}
-    return world
-
-
-def get_world(cur, id):
-    cur.execute('EXECUTE get_world (%s)', (id,))
-    result = cur.fetchone()
-    return {'id': result[0], 'randomNumber': result[1]}
-
-
[email protected]("/queries")
-def test_queries(request):
-    with db.cursor() as cur:
-        worlds = [get_world(cur, id) for id in generate_ids(get_num_queries(request))]
-    return worlds
-
-
[email protected]("/fortunes")
-def test_fortunes(request):
-    with db.cursor() as cur:
-        cur.execute('EXECUTE fortune')
-        fortunes = list(cur.fetchall())
-    fortunes.append((0, 'Additional fortune added at request time.'))
-    fortunes.sort(key=lambda f: f[1])
-    return render_template("fortunes_raw.html", fortunes=fortunes), {'Content-Type': 'text/html; charset=utf-8'}
-
-
[email protected]("/updates")
-def test_updates(request):
-    worlds = []
-    updated_worlds = []
-    with db.cursor() as cur:
-        for id in generate_ids(get_num_queries(request)):
-            cur.execute('EXECUTE get_world (%s)', (id,))
-            result = cur.fetchone()
-            new_value = randint(1, 10000)
-            updated_worlds.append((new_value, result[0]))
-            worlds.append({'id': result[0], 'randomNumber': new_value})
-        execute_batch(cur, 'EXECUTE update_world (%s, %s)', updated_worlds)
-        db.commit()
-    return worlds
-
-
[email protected]("/plaintext")
-def test_plaintext(request):
-    return b"Hello, World!"
-
-
-@cached(cache={}, key=lambda cur, id: hashkey(id))
-def get_cached_world(cur, id):
-    cur.execute('EXECUTE get_world (%s)', (id,))
-    result = cur.fetchone()
-    return {'id': result[0], 'randomNumber': result[1]}
-
-
[email protected]("/cached-queries")
-def test_cached_queries(request):
-    with db.cursor() as cur:
-        worlds = [get_cached_world(cur, id) for id in generate_ids(get_num_queries(request, 'count'))]
-    return worlds

+ 4 - 48
frameworks/Python/microdot/benchmark_config.json

@@ -19,14 +19,14 @@
         "flavor": "None",
         "orm": "Full",
         "platform": "None",
-        "webserver": "Gunicorn",
+        "webserver": "Uvicorn",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "Microdot-WSGI",
+        "display_name": "Microdot",
         "notes": "",
         "versus": "None"
       },
-      "async": {
+      "raw": {
         "json_url": "/json",
         "db_url": "/db",
         "query_url": "/queries?queries=",
@@ -46,51 +46,7 @@
         "webserver": "Uvicorn",
         "os": "Linux",
         "database_os": "Linux",
-        "display_name": "Microdot-ASGI",
-        "notes": "",
-        "versus": "None"
-      },
-      "raw": {
-        "db_url": "/db",
-        "query_url": "/queries?queries=",
-        "fortune_url": "/fortunes",
-        "update_url": "/updates?queries=",
-	      "cached_query_url": "/cached-queries?count=",
-        "port": 8080,
-        "approach": "Realistic",
-        "classification": "Micro",
-        "database": "postgres",
-        "framework": "Microdot",
-        "language": "Python",
-        "flavor": "None",
-        "orm": "Raw",
-        "platform": "None",
-        "webserver": "Gunicorn",
-        "os": "Linux",
-        "database_os": "Linux",
-        "display_name": "Microdot-WSGI-Raw",
-        "notes": "",
-        "versus": "None"
-      },
-      "async-raw": {
-        "db_url": "/db",
-        "query_url": "/queries?queries=",
-        "fortune_url": "/fortunes",
-        "update_url": "/updates?queries=",
-	      "cached_query_url": "/cached-queries?count=",
-        "port": 8080,
-        "approach": "Realistic",
-        "classification": "Micro",
-        "database": "postgres",
-        "framework": "Microdot",
-        "language": "Python",
-        "flavor": "None",
-        "orm": "Raw",
-        "platform": "None",
-        "webserver": "Uvicorn",
-        "os": "Linux",
-        "database_os": "Linux",
-        "display_name": "Microdot-ASGI-Raw",
+        "display_name": "Microdot-Raw",
         "notes": "",
         "versus": "None"
       }

+ 0 - 15
frameworks/Python/microdot/gunicorn_conf.py

@@ -1,15 +0,0 @@
-import multiprocessing
-import os
-import sys
-
-_is_pypy = hasattr(sys, "pypy_version_info")
-_is_travis = os.environ.get("TRAVIS") == "true"
-
-workers = int(multiprocessing.cpu_count() * 4)
-if _is_travis:
-    workers = 2
-
-bind = "0.0.0.0:8080"
-keepalive = 120
-errorlog = "-"
-pidfile = "gunicorn.pid"

+ 0 - 15
frameworks/Python/microdot/microdot-async-raw.dockerfile

@@ -1,15 +0,0 @@
-FROM python:3.11-buster
-
-RUN apt-get update
-RUN apt-get install libpq-dev python3-dev -y
-ADD ./requirements.txt /microdot/requirements.txt
-RUN pip3 install -r /microdot/requirements.txt
-ADD ./ /microdot
-WORKDIR /microdot
-
-ENV PYTHONUNBUFFERED 1
-ENV DATABASE_URL postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world
-
-EXPOSE 8080
-
-CMD gunicorn app_async_raw:asgi -c uvicorn_conf.py

+ 0 - 15
frameworks/Python/microdot/microdot-async.dockerfile

@@ -1,15 +0,0 @@
-FROM python:3.11-buster
-
-RUN apt-get update
-RUN apt-get install libpq-dev python3-dev -y
-ADD ./requirements.txt /microdot/requirements.txt
-RUN pip3 install -r /microdot/requirements.txt
-ADD ./ /microdot
-WORKDIR /microdot
-
-ENV PYTHONUNBUFFERED 1
-ENV DATABASE_URL postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world
-
-EXPOSE 8080
-
-CMD gunicorn app_async:app -c uvicorn_conf.py

+ 3 - 3
frameworks/Python/microdot/microdot-raw.dockerfile

@@ -1,4 +1,4 @@
-FROM python:3.11-buster
+FROM python:3.12-slim
 
 RUN apt-get update
 RUN apt-get install libpq-dev python3-dev -y
@@ -8,8 +8,8 @@ ADD ./ /microdot
 WORKDIR /microdot
 
 ENV PYTHONUNBUFFERED 1
-ENV DATABASE_URL "host=tfb-database port=5432 user=benchmarkdbuser password=benchmarkdbpass dbname=hello_world"
+ENV DATABASE_URL postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/hello_world
 
 EXPOSE 8080
 
-CMD gunicorn app_sync_raw:app -c gunicorn_conf.py
+CMD gunicorn app_raw:asgi -c uvicorn_conf.py

+ 2 - 2
frameworks/Python/microdot/microdot.dockerfile

@@ -1,4 +1,4 @@
-FROM python:3.11-buster
+FROM python:3.12-slim
 
 RUN apt-get update
 RUN apt-get install libpq-dev python3-dev -y
@@ -12,4 +12,4 @@ ENV DATABASE_URL postgresql://benchmarkdbuser:benchmarkdbpass@tfb-database:5432/
 
 EXPOSE 8080
 
-CMD gunicorn app_sync:app -c gunicorn_conf.py
+CMD gunicorn app:app -c uvicorn_conf.py

+ 1 - 1
frameworks/Python/microdot/requirements.txt

@@ -7,6 +7,6 @@ cachetools
 asyncache
 
 alchemical
-microdot<2
+microdot>=2.0.1
 gunicorn
 uvicorn[standard]

+ 0 - 1
frameworks/Python/microdot/uvicorn_conf.py

@@ -1,6 +1,5 @@
 import multiprocessing
 import os
-import sys
 
 _is_travis = os.environ.get("TRAVIS") == "true"