uv
.https://docs.astral.sh/uv/#installation
Attention à toujours se positionner au même niveau que le
pyproject.toml
avant de lancer une commande uv
:
$ uv sync # pour installer les dépendances
$ uv run archilog # lancement de l'application
Plus d’info sur https://docs.astral.sh/uv/.
Click is a Python package for creating beautiful command line interfaces in a composable way with as little code as necessary.
import click
@click.command()
@click.option("--count", default=1)
@click.option("--name", prompt="Your name")
def hello(count, name):
print(name, count)
$ uv run archilog hello --count=3
Your name: John
John 3
SQLite is a C library that provides a lightweight disk-based database that doesn’t require a separate server process.
import sqlite3
= sqlite3.connect("tutorial.db")
db "CREATE TABLE movie(title, year, score)")
db.execute(
= db.execute("SELECT title FROM movie").fetchone() result
SQLAlchemy est un toolkit open source SQL et un mapping objet-relationnel (ORM) écrit en Python et publié sous licence MIT.
= create_engine("sqlite:///data.db", echo=True)
engine = MetaData()
metadata
= Table(
users_table "users",
metadata,"id", Uuid, primary_key=True, default=uuid.uuid4),
Column("login", String, nullable=False)
Column( )
= users_table.insert().values(login="john.doe")
stmt
with engine.begin() as conn:
= conn.execute(stmt) result
$ uv add sqlalchemy
Flask is a lightweight WSGI web application framework. It is designed to make getting started quick and easy, with the ability to scale up to complex applications.
# inside archilog/views.py
from flask import Flask
= Flask(__name__)
app
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
$ uv run flask --app archilog.views --debug run
* Serving Flask app 'archilog.views'
* Debug mode: on
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
from flask import render_template
@app.route("/hello/")
@app.route("/hello/<name>")
def hello(name=None):
return render_template("hello.html", name=name)
<!doctype html>
<title>Hello from Flask</title>
{% if name %}<h1>Hello {{ name }}!</h1>
{% else %}<h1>Hello, World!</h1>
{% endif %}
.
├── pyproject.toml
├── README.md
└── src
└── archilog
├── __init__.py
├── models.py
├── services.py
├── templates
│ └── home.html
└── views.py
$ uv add flask
from flask import Blueprint, render_template
= Blueprint("web_ui", __name__)
web_ui
@web_ui.route("/<page>")
def show(page):
return render_template(f"pages/{page}.html")
def create_app():
= Flask(__name__)
app
from archilog.views import api, web_ui
="/")
app.register_blueprint(web_ui, url_prefix="/api")
app.register_blueprint(api, url_prefix
return app
Rien ne change, Flask cherche une fonction create_app
:
$ uv run flask --app archilog.views --debug run
= create_engine("sqlite:///data.db", echo=True)
engine = MetaData() metadata
from archilog import config
= create_engine(config.DATABASE_URL, echo=config.DEBUG)
engine = MetaData() metadata
from dataclasses import dataclass
@dataclass
class Config:
str
DATABASE_URL: bool
DEBUG:
= Config(
config ="sqlite:///data.db",
DATABASE_URL=True
DEBUG )
[…] commonly used to store configuration settings and sensitive information in a simple text file, typically named .env.
Dans un fichier dev.env
:
ARCHILOG_DATABASE_URL=sqlite:///data.db
ARCHILOG_DEBUG=True
ARCHILOG_FLASK_SECRET_KEY=secret!
$ env $(cat dev.env | xargs) uv run flask --app ...
Dans le fichier pyproject.toml
:
[tool.pyproject-runner.tasks]
start = {
cmd = "flask --app archilog.views --debug run",
env-file = "!/dev.env"
}
$ uv run rr start
import os
[...]
= Config(
config =os.getenv(
DATABASE_URL"ARCHILOG_DATABASE_URL", "sqlite:///data.db"
),=os.getenv("ARCHILOG_DEBUG", "False") == "True"
DEBUG )
="ARCHILOG_FLASK") app.config.from_prefixed_env(prefix
$ uv add --dev pyproject-runner
Syntaxique : le type de la donnée est respecté (date, entier, texte, etc).
Sémantique : les données ont du sens (date de début inférieur à la date de fin, prix supérieur à 0, etc).
[…] a flexible forms validation and rendering library for Python web development.
from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired
class MyForm(FlaskForm):
= StringField('name', validators=[DataRequired()]) name
<form method="POST" action="/">
{{ form.csrf_token }}
{{ form.name.label }} {{ form.name(size=20) }}<input type="submit" value="Go">
</form>
@app.route('/submit', methods=['GET', 'POST'])
def submit():
= MyForm()
form if form.validate_on_submit():
return redirect('/success')
return render_template('submit.html', form=form)
import logging
"Watch out!") # will print to the console
logging.warning("I told you so") # will not print anything logging.info(
import logging
logging.basicConfig(=logging.INFO,
levelformat="%(asctime)s [%(levelname)s] %(message)s",
=[
handlers"archilog.log"),
logging.FileHandler(
logging.StreamHandler()
] )
@app.errorhandler(500)
def handle_internal_error(error):
"Erreur interne du serveur", "error")
flash(
logging.exception(error)return redirect(url_for(".home"))
from flask import abort, render_template
@app.get("/users/create")
def users_create_form():
500) # raise a 500 Internal Server Error exception
abort(return render_template("users_create_form.html")
$ uv add flask-wtf
[…] a Flask extension that simplifies the use of HTTP authentication with Flask routes.
from flask import Flask
from flask_httpauth import HTTPBasicAuth
from werkzeug.security import generate_password_hash
= Flask(__name__)
app = HTTPBasicAuth()
auth
= {
users "john": generate_password_hash("hello"),
"susan": generate_password_hash("bye")
}
from werkzeug.security import check_password_hash
@auth.verify_password
def verify_password(username, password):
if username in users and \
check_password_hash(users.get(username), password):return username
@auth.get_user_roles
def get_user_roles(username):
return get_roles(username)
@app.route("/")
@auth.login_required
def index():
return f"Hello, {auth.current_user()}!"
@app.route("/admin")
@auth.login_required(role="admin")
def admins_only():
return f"Hello {auth.current_user()}, you are an admin!"
$ uv add flask-httpauth
admin
qui accède à tout ;user
qui n’est qu’en lecture seule.Pour se déconnecter, nettoyer les données du navigateur avec
ctrl+shift+del
.
Une API s’appuyant sur HTTP est définie par :
Souvent appelée à tort une API REST ou RESTful.
Manipulation d’une ressource via les verbes HTTP.
Verbe | Ressource | Signification |
---|---|---|
GET |
/users |
récupère toutes les entités de la collection |
POST |
/users |
crée une entité dans la collection |
GET |
/users/13 |
récupère une entité |
PUT |
/users/13 |
met à jour entièrement l’entité |
PATCH |
/users/13 |
met à jour partiellement l’entité |
DELETE |
/users/13 |
supprime l’entité |
GET |
/users?inactive=true |
récupère toutes les entités inactive |
GET |
/users?role=admin |
récupère toutes les entités admin |
Appel d’une fonction via un POST + paramètres.
Verbe | Ressource |
---|---|
POST |
/notify?channel=archilog&priority=critical |
POST |
/drafts/42/publish |
Dans la majorité des cas, ce sera au format JSON
,
ensuite vient le xml
.
{
"name": "john",
"age": "42"
}
user>
<name>john</name>
<age>42</age>
<user> </
API spec validator and OpenAPI document generator for Python web frameworks.
from flask import Blueprint
from spectree import SpecTree
= Blueprint("api", __name__)
api = SpecTree("flask") api_spec
from pydantic import BaseModel, Field
class UserData(BaseModel):
str = Field(min_length=2, max_length=40)
name: int = Field(gt=0, lt=150)
age:
@api.post("/users")
@api_spec.validate(tags=["api"])
def create_user(json: UserData):
return {"username": json.name}
from flask_httpauth import HTTPTokenAuth
= HTTPTokenAuth(scheme='Bearer')
auth
@auth.verify_token
def verify_token(token):
pass
$ curl http://localhost:5000/api/users \
-H "Accept: application/json" \
-H "Authorization: Bearer {token}"
Réponse :
[
{"name": "john", "age": "42"},
{"name": "jane", "age": "34"}
]
= Blueprint("api", __name__)
api = SpecTree("flask", app=api) api_spec
Puis se rendre sur /api/apidoc/swagger
.
from spectree import SpecTree, SecurityScheme
= SpecTree(
api_spec "flask",
=api,
app=[
security_schemes
SecurityScheme(="bearer_token",
name={"type": "http", "scheme": "bearer"}
data
)
],=[{"bearer_token": []}]
security )
api
:
Blueprint
;$ uv add --dev gunicorn
$ uv run gunicorn archilog.views:create_app()
$ uv build
$ ls dist/
archilog-0.2-py3-none-any.whl archilog-0.2.tar.gz
FROM python:3
WORKDIR /app
COPY dist/archilog-*-py3-none-any.whl /app/
RUN pip install *.whl gunicorn
CMD ["gunicorn", "archilog.views:create_app()"]
Avec buildah
/podman
ou docker
:
$ docker build -t my-username/my-image .
$ docker run --name mycontainer my-username/my-image
$ docker exec -d mycontainer sh -c "archilog init-db"