uv, un gestionnaire de paquets/projet.https://docs.astral.sh/uv/#installation
Attention à toujours se positionner au même niveau que le
pyproject.toml avant de lancer une commande :
$ uv sync # pour installer les dépendances
$ uv run archilog # lancement de l'applicationPlus 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 3SQLite is a C library that provides a lightweight disk-based database that doesn’t require a separate server process.
import sqlite3
db = sqlite3.connect("tutorial.db")
db.execute("CREATE TABLE movie(title, year, score)")
result = db.execute("SELECT title FROM movie").fetchone()sqlite ;click.SQLAlchemy est un toolkit open source SQL et un mapping objet-relationnel (ORM) écrit en Python et publié sous licence MIT.
engine = create_engine("sqlite:///data.db", echo=True)
metadata = MetaData()
users_table = Table(
"users",
metadata,
Column("id", Uuid, primary_key=True, default=uuid.uuid4),
Column("login", String, nullable=False)
)stmt = users_table.insert().values(login="john.doe")
with engine.begin() as conn:
result = conn.execute(stmt)$ uv add sqlalchemyFlask 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
app = Flask(__name__)
@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 quitfrom 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
├── data.py
├── domain.py
├── templates
│ └── home.html
└── views.py$ uv add flaskfrom flask import Blueprint, render_template
web_ui = Blueprint("web_ui", __name__)
@web_ui.route("/")
def show():
return render_template("index.html")def create_app():
app = Flask(__name__)
from archilog.views import api, web_ui
app.register_blueprint(web_ui, url_prefix="/")
app.register_blueprint(api, url_prefix="/api")
return appRien ne change, Flask cherche une fonction create_app
:
$ uv run flask --app archilog.views --debug run# before
engine = create_engine("sqlite:///data.db", echo=True)
# after
from archilog import config
engine = create_engine(config.DATABASE_URL, echo=config.DEBUG)from dataclasses import dataclass
@dataclass
class Config:
DATABASE_URL: str
DEBUG: bool
config = Config(
DATABASE_URL="sqlite:///data.db",
DEBUG=True
)import os
config = Config(
DATABASE_URL=os.getenv(
"ARCHILOG_DATABASE_URL", "sqlite:///data.db"
),
DEBUG=os.getenv("ARCHILOG_DEBUG", "False") == "True"
)# inside dev.env
ARCHILOG_DATABASE_URL=sqlite:///data.db
ARCHILOG_DEBUG=TruePuis dans un terminal :
$ env $(cat dev.env | xargs) uv run ...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):
name = StringField('name', validators=[DataRequired()])<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():
form = MyForm()
if form.validate_on_submit():
return redirect('/success')
return render_template('submit.html', form=form)import logging
logging.warning("Watch out!") # will print to the console
logging.info("I told you so") # will not print anythingimport logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[
logging.FileHandler("archilog.log"),
logging.StreamHandler()
]
)@app.errorhandler(500)
def handle_internal_error(error):
flash("Erreur interne du serveur", "error")
logging.exception(error)
return redirect(url_for(".home"))from flask import abort, render_template
@app.get("/users/create")
def users_create_form():
abort(500) # raise a 500 Internal Server Error exception
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
app = Flask(__name__)
auth = HTTPBasicAuth()
# in-memory users database
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-httpauthadmin qui accède à tout sans aucune
restrictions ;user qui ne peut pas créer ou supprimer de
cagnotte.Pour se déconnecter, nettoyer les données du navigateur avec
ctrl+shift+del ou utiliser la naigation privée de votre
navigateur.
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
api = Blueprint("api", __name__)
api_spec = SpecTree("flask")from pydantic import BaseModel, Field
class UserData(BaseModel):
name: str = Field(min_length=2, max_length=40)
age: int = Field(gt=0, lt=150)
@api.post("/users")
@api_spec.validate(tags=["api"])
def create_user(json: UserData):
return {"username": json.name}from flask_httpauth import HTTPTokenAuth
auth = HTTPTokenAuth(scheme='Bearer')
@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"}
]api = Blueprint("api", __name__)
api_spec = SpecTree("flask", app=api)Puis se rendre sur /api/apidoc/swagger.
api_spec = SpecTree(
"flask",
app=api,
security_schemes=[
SecurityScheme(
name="bearer_token",
data={"type": "http", "scheme": "bearer"}
)
],
security=[{"bearer_token": []}]
)Ajouter une nouvelle vue api :
json ;json
;$ uv add --dev granian
$ uv run granian --interface wsgi archilog.views:create_app$ uv build
$ ls dist/
archilog-0.2-py3-none-any.whl archilog-0.2.tar.gzFROM python:3.14-slim
WORKDIR /app
COPY dist/archilog-*-py3-none-any.whl /tmp/
RUN pip install --no-cache-dir \
/tmp/archilog-*-py3-none-any.whl \
granian
CMD ["granian", "--interface", "wsgi", "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"