
virtualenv
Documentation : https://virtualenv.pypa.io/en/latest/
Journaux liées à cette note :
Journal du mercredi 05 février 2025 à 18:32
Un ami m'a fait découvrir uv (https://github.com/astral-sh/uv).
Je trouve cela amusant de constater que Rust prend en charge de plus en plus d'outils pour différents langages 😉.
Le projet a commencé fin 2023.
Voici un thread Hacker News de 200 commentaires à ce sujet qui date de février 2024 : Uv: Python packaging in Rust .
L'article de ce thread contient beaucoup d'éléments intéressants : https://astral.sh/blog/uv
Son nom uv
semble être une référence à uvloop.
J'en ai profité pour migrer le playground mise-python-flask-playground
de pip vers uv : https://github.com/stephane-klein/mise-python-flask-playground/commit/2f1678798cfc6749dcfdb514a8fe4a3e54739844.
J'ai lancé une installation et effectivement, sa rapidité est très impressionnante :
$ uv pip install -r requirements.txt
Resolved 15 packages in 245ms
Prepared 15 packages in 176ms
Installed 15 packages in 37ms
+ alembic==1.14.1
+ blinker==1.9.0
+ click==8.1.8
+ flask==3.1.0
+ flask-migrate==4.1.0
+ flask-sqlalchemy==3.1.1
+ greenlet==3.1.1
+ itsdangerous==2.2.0
+ jinja2==3.1.5
+ mako==1.3.9
+ markupsafe==3.0.2
+ psycopg2-binary==2.9.10
+ sqlalchemy==2.0.37
+ typing-extensions==4.12.2
+ werkzeug==3.1.3
uv ne propose pas seulement une amélioration de l'installation de packages Python, mais propose beaucoup d'autres choses comme :
- L'installation de Python : https://docs.astral.sh/uv/guides/install-python/
Pour cette partie, dans un but d'unification, je continuerai à utiliser Mise pour installer une version précise de Python. De plus, Mise intègre nativement UV : https://mise.jdx.dev/mise-cookbook/python.html#mise-uv
- Système pour lancer des scripts Python : https://docs.astral.sh/uv/guides/scripts/
Exemple :
$ uv run example.py
Je pense avoir compris que cela lance ce script avec les dépendances du virtual environment du projet. Un peu comme fonctionne npm, yarn ou pnpm qui permet aux scripts d'utiliser les packages présents dans ./node_modules/
.
- Permet de lancer des outils : https://docs.astral.sh/uv/guides/tools/
Par exemple, le linter Python ruff, exemple :
$ uv tool run ruff
- uv met à disposition des images Docker : https://docs.astral.sh/uv/guides/integration/docker/
J'ai un peu parcouru la documentation de pyproject.toml : https://packaging.python.org/en/latest/guides/writing-pyproject-toml/.
J'ai lu aussi la section uv - Locking environments.
Suite à ces lectures, j'ai migré le playground mise-python-flask-playground
vers pyproject.toml : https://github.com/stephane-klein/mise-python-flask-playground/commit/c17216464778df4bc00bf782d5a889cb3f198051.
Je ne suis pas certain que ces commandes soient une bonne pratique :
$ uv pip compile requirements.in -o requirements.txt
$ uv pip install -r requirements.txt
Playground qui présente comment je setup un projet Python Flask en 2025
Je pense que cela doit faire depuis 2015 que je n'ai pas développé une application en Python Flask !
Entre 2008 et 2015, j'ai beaucoup itéré dans mes méthodes d'installation et de setup de mes environnements de développement Python.
D'après mes souvenirs, si je devais dresser la liste des différentes étapes, ça donnerai ceci :
- 2006 : aucune méthode, j'installe Python 🙂
- 2007 : je me bats avec setuptools et distutils (mais ça va, c'était plus mature que ce que je pouvais trouver dans le monde PHP qui n'avait pas encore imaginé composer)
- 2008 : je trouve la paie avec virtualenv
- 2010 : j'ai peur d'écrire des scripts en Bash alors à la place, j'écris un script
bootstrap.py
dans lequel j'essaie d'automatiser au maximum l'installation du projet - 2012 : je me bats avec buildout pour essayer d'automatiser des éléments d'installation. Avec le recul, je réalise que je n'ai jamais rien compris à buildout
- 2012 : j'utilise Vagrant pour fixer les éléments d'installation, je suis plutôt satisfait
- 2015 : je suis radicale, j'enferme tout l'environnement de dev Python dans un container de développement, je monte un path volume pour exposer le code source du projet dans le container. Je bricole en
entrypoint
avec la commande "sleep".
Des choses ont changé depuis 2015.
Mais, une chose que je n'ai pas changée, c'est que je continue à suivre le modèle The Twelve-Factors App et je continue à déployer tous mes projets packagé dans des images Docker. Généralement avec un simple docker-compose.yml
sur le serveur, ou alors Kubernetes pour des projets de plus grande envergure… mais cela ne m'arrive jamais en pratique, je travaille toujours sur des petits projets.
Choses qui ont changé : depuis fin 2018, j'ai décidé de ne plus utiliser Docker dans mes environnements de développement pour les projets codés en NodeJS, Golang, Python…
Au départ, cela a commencé par uniquement les projets en NodeJS pour des raisons de performance.
J'ai ensuite découvert Asdf et plus récemment Mise. À partir de cela, tout est devenu plus facilement pour moi.
Avec Asdf, je n'ai plus besoin "d'enfermer" mes projets dans des containers Docker pour fixer l'environnement de développement, les versions…
Cette introduction est un peu longue, je n'ai pas abordé le sujet principal de cette note 🙂.
Je viens de publier un playground d'un exemple de projet minimaliste Python Flask suivant mes pratiques de 2025.
Voici son repository : mise-python-flask-playground
Ce playground est "propulsé" par Docker et Mise.
J'ai documenté la méthode d'installation pour :
- Linux (Fedora (distribution que j'utilise au quotidien) et Ubuntu)
- MacOS avec Brew
- MS Windows avec WSL2
Je précise que je n'ai pas eu l'occasion de tester l'installation sous Windows, hier j'ai essayé, mais je n'ai pas réussi à installer WSL2 sous Windows dans un Virtualbox lancé sous Fedora. Je suis à la recherche d'une personne pour tester si mes instructions d'installation sont valides ou non.
Briques technologiques présentes dans le playground :
- La dernière version de Python installée par Mise, voir .mise.toml
- Une base de données PostgreSQL lancé par Docker
- J'utilise named volumes comme expliqué dans cette note : 2024-12-09_1550
- Flask-SQLAlchemy
- Flask-Migrate
- Une commande
flask initdb
avec Click pour reset la base de données - Utiliser d'un template Jinja2 pour qui affiche les
users
en base de données
Voici quelques petites subtilités.
Dans le fichier alembic.ini j'ai modifié le paramètre file_template
parce que j'aime que les fichiers de migration soient classés par ordre chronologique :
[alembic]
# template used to generate migration files
file_template = %%(year)d%%(month).2d%%(day).2d_%%(hour).2d%%(minute).2d%%(second).2d_%%(slug)s
20250205_124639_users.py
20250205_125437_add_user_lastname.py
Ici le port de PostgreSQL est généré dynamiquement par docker compose :
postgres:
image: postgres:17
...
ports:
- 5432 # <= ici
Avec cela, fini les conflits de port quand je lance plusieurs projets en même temps sur ma workstation.
L'URL vers le serveur PostgreSQL est générée dynamiquement par le script get_postgres_url.sh
qui est appelé par le fichier .envrc
. Tout cela se passe de manière transparente.
J'initialise ici les extensions PostgreSQL :
def init_db():
db.drop_all()
db.session.execute(db.text('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"'))
db.session.execute(db.text('CREATE EXTENSION IF NOT EXISTS "unaccent"'))
db.session.commit()
db.create_all()
et ici dans la première migration :
def upgrade():
op.execute('CREATE EXTENSION IF NOT EXISTS "uuid-ossp";')
op.execute('CREATE EXTENSION IF NOT EXISTS "unaccent";')
op.create_table('users',
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
sa.Column('firstname', sa.String(), nullable=False),
sa.PrimaryKeyConstraint('id')
)