Sådan gør du din kode hurtig og asynkron med Python og Sanic

Hej allesammen. I denne artikel vil jeg tale om at opbygge enkle asynkrone projekter med Sanic-rammen.

Introduktion

Sanicer en meget kolbe-lignende open source Python-webserver og web-framework med mere end 10K stjerner, der er skrevet for at gå hurtigt. Det tillader brug af async/awaitsyntaks tilføjet i Python 3.5 (læs mere), hvilket gør din kode ikke-blokerende og hurtig.

Sanic har ret god dokumentation, og den vedligeholdes af samfundet for samfundet.

Målet med projektet er at give en enkel måde at få en højtydende HTTP-server i gang, der er let at opbygge, udvide og i sidste ende skalere.

Krav

Lad os, inden vi starter, installere nogle pakker og sørge for, at vi har alt klar til udvikling af dette projekt.

Bemærk: Kildekode er tilgængelig i mit github.com-arkiv. For hvert trin er der en tilsvarende forpligtelse.

Forudsætninger:

  • Python3.6 +
  • pipenv (du kan bruge enhver anden pakkeinstallatør)
  • PostgreSQL(til database kan det også være MySQL eller SQLite)

Pakker:

  • sikkert er en let pakke, der tilføjer valgfri sikkerhedsoverskrifter og cookieattributter til Python-webrammer.
  • environs er et Python-bibliotek til parsing af miljøvariabler. Det giver dig mulighed for at gemme konfiguration adskilt fra din kode i henhold til The Twelve-Factor App-metoden.
  • sanic-envconfig er og udvidelse, der hjælper dig med at bringe kommandolinje- og miljøvariabler ind i din Sanic-konfiguration.
  • databaser er en Python-pakke, der giver dig mulighed for at foretage forespørgsler ved hjælp af det kraftfulde SQLAlchemy Core-ekspressionssprog og giver support til PostgreSQL, MySQL og SQLite.

Lad os oprette en tom mappe og initialisere en tom Pipfileder.

pipenv  -- python python3.6

Installer alle nødvendige pakker ved hjælp af pipenv- kommandoer nedenfor.

pipenv install sanic secure environs sanic-envconfig

Til database:

pipenv install databases[postgresql]

Valg erpostgresql, mysql, sqlite.

Struktur

Lad os nu oprette nogle filer og mapper, hvor vi skriver vores faktiske kode.

├── .env├── Pipfile├── Pipfile.lock├── setup.py└── project ├── __init__.py ├── __main__.py ├── main.py ├── middlewares.py ├── routes.py ├── settings.py └── tables.py

Vi bruger setup.pyfilen til at gøre projectmappen tilgængelig som en pakke i vores kode.

from setuptools import setupsetup( name="project",)

Installerer ...

pipenv install -e .

I .envfilen gemmer vi nogle globale variabler som f.eks. URL'en til databaseforbindelsen.

__main__.pyer oprettet til at gøre vores projectpakke eksekverbar fra kommandolinjen.

pipenv run python -m project

Initialisering

Lad os foretage vores første opkald i __main__ . py- filen.

from project.main import initinit()

Dette er begyndelsen på vores ansøgning. Nu skal vi oprette initfunktionen inde i main.py- filen.

from sanic import Sanicapp = Sanic(__name__)def init(): app.run(host='0.0.0.0', port=8000, debug=True)

Ved blot at oprette appen fra Sanic- klassen kan vi køre den med angivelse af værts- , port- og valgfri debug- søgeordsargument.

Kører ...

pipenv run python -m project

Sådan skal en vellykket output se ud i din Sanic-app. Hvis du åbner //0.0.0.0:8000 i din browser vil du se

Fejl: Anmodet URL / ikke fundet

Vi har ikke oprettet nogen ruter endnu, så det er fint for nu. Vi tilføjer nogle ruter nedenfor.

Indstillinger

Nu kan vi ændre miljø og indstillinger. Vi er nødt til at tilføje nogle variabler i .env- filen, læse dem og videregive til Sanic app config.

.env- fil.

DEBUG=TrueHOST=0.0.0.0POST=8000

Konfiguration ...

from sanic import Sanic
from environs import Envfrom project.settings import Settings
app = Sanic(__name__)
def init(): env = Env() env.read_env() app.config.from_object(Settings) app.run( host=app.config.HOST, port=app.config.PORT, debug=app.config.DEBUG, auto_reload=app.config.DEBUG, )

indstillinger.py- fil.

from sanic_envconfig import EnvConfigclass Settings(EnvConfig): DEBUG: bool = True HOST: str = '0.0.0.0' PORT: int = 8000

Bemærk, at jeg har tilføjet et valgfrit auto_reload- argument, der aktiverer eller deaktiverer den automatiske genindlæser.

Database

Nu er det tid til at konfigurere en database.

En lille note om databasepakken , inden vi går videre:

databasepakken bruger asyncpg som er et asynkront interface-bibliotek til PostgreSQL. Det er ret hurtigt. Se resultater nedenfor.

Vi bruger to af Sanics lyttere, hvor vi specificerer databaseforbindelses- og frakoblingshandlinger. Her er de lyttere, som vi skal bruge:

  • after_server_start
  • after_server_stop

main.py file.

from sanic import Sanic
from databases import Database
from environs import Envfrom project.settings import Settings
app = Sanic(__name__)
def setup_database(): app.db = Database(app.config.DB_URL) @app.listener('after_server_start') async def connect_to_db(*args, **kwargs): await app.db.connect() @app.listener('after_server_stop') async def disconnect_from_db(*args, **kwargs): await app.db.disconnect()
def init(): env = Env() env.read_env() app.config.from_object(Settings)
 setup_database()
 app.run( host=app.config.HOST, port=app.config.PORT, debug=app.config.DEBUG, auto_reload=app.config.DEBUG, )

Once more thing. We need to specify DB_URL in project settings and environment.

.env file.

DEBUG=TrueHOST=0.0.0.0POST=8000DB_URL=postgresql://postgres:[email protected]/postgres

And settings.py file.

from sanic_envconfig import EnvConfigclass Settings(EnvConfig): DEBUG: bool = True HOST: str = '0.0.0.0' PORT: int = 8000 DB_URL: str = ''

Make sure that DB_URL is correct and your database is running. Now you can access to database using app.db. See more detailed information in the next section.

Tables

Now we have a connection to our database and we can try to do some SQL queries.

Let’s declare a table in tables.py file using SQLAlchemy.

import sqlalchemymetadata = sqlalchemy.MetaData()books = sqlalchemy.Table( 'books', metadata, sqlalchemy.Column('id', sqlalchemy.Integer, primary_key=True), sqlalchemy.Column('title', sqlalchemy.String(length=100)), sqlalchemy.Column('author', sqlalchemy.String(length=60)),)

Here I assume that you already have a migrated database with a books table in it. For creating database migrations, I recommend that you use Alembic which is a lightweight and easy-to-use tool that you can use with the SQLAlchemy Database Toolkit for Python.

Now we can use any SQLAlchemy core queries. Check out some examples below.

# Executing manyquery = books.insert()values = [ {"title": "No Highway", "author": "Nevil Shute"}, {"title": "The Daffodil", "author": "SkyH. E. Bates"},]await app.db.execute_many(query, values)# Fetching multiple rowsquery = books.select()rows = await app.db.fetch_all(query)# Fetch single rowquery = books.select()row = await app.db.fetch_one(query)

Routes

Now we need to setup routes. Let’s go to routes.py and add a new route for books.

from sanic.response import json
from project.tables import books
def setup_routes(app): @app.route("/books") async def book_list(request): query = books.select() rows = await request.app.db.fetch_all(query) return json({ 'books': [{row['title']: row['author']} for row in rows] })

Of course we need to call setup_routes in init to make it work.

from project.routes import setup_routes
app = Sanic(__name__)
def init(): ... app.config.from_object(Settings) setup_database() setup_routes(app) ...

Requesting…

$ curl localhost:8000/books{"books":[{"No Highway":"Nevil Shute"},{"The Daffodil":"SkyH. E. Bates"}]}

Middlewares

What about checking the response headers and seeing what we can add or fix there?

$ curl -I localhost:8000Connection: keep-aliveKeep-Alive: 5Content-Length: 32Content-Type: text/plain; charset=utf-8

As you can see we need some security improvements. There are some missing headers such as X-XSS-Protection, Strict-Transport-Securityso let’s take care of them using a combination of middlewares and secure packages.

middlewares.py file.

from secure import SecureHeaderssecure_headers = SecureHeaders()def setup_middlewares(app): @app.middleware('response') async def set_secure_headers(request, response): secure_headers.sanic(response)

Setting up middlewares in main.py file.

from project.middlewares import setup_middlewares
app = Sanic(__name__)
def init(): ... app.config.from_object(Settings) setup_database() setup_routes(app) setup_middlewares(app) ...

The result is:

$ curl -I localhost:8000/booksConnection: keep-aliveKeep-Alive: 5Strict-Transport-Security: max-age=63072000; includeSubdomainsX-Frame-Options: SAMEORIGINX-XSS-Protection: 1; mode=blockX-Content-Type-Options: nosniffReferrer-Policy: no-referrer, strict-origin-when-cross-originPragma: no-cacheExpires: 0Cache-control: no-cache, no-store, must-revalidate, max-age=0Content-Length: 32Content-Type: text/plain; charset=utf-8

As I promised at the beginning, there is a github repository for each section in this article. Hope this small tutorial helped you to get started with Sanic. There are still many unexplored features in the Sanic framework that you can find and check out in the documentation.

davitovmasyan/sanic-project

Goin' Fast and asynchronous with Python and Sanic! - davitovmasyan/sanic-projectgithub.com

Hvis du har tanker om dette, skal du sørge for at efterlade en kommentar.

Giv mig nogle klapper, hvis du fandt denne artikel hjælpsom?

Tak for læsningen. Gå hurtigt med Sanic og held og lykke !!!