In the last 5 years, Python development has improved leaps and bounds. I am primarily a JavaScript developer, so I have been pleased to see the growth in tools that provide version and dependency management, formatting, linting and type safety.
TL;DR: I use mise
for Python version management and virtual environments, poetry
or uv
for dependency management, ruff
for formatting and linting, and pydantic
for runtime schemas.
I use mise
from https://mise.jdx.dev/lang/python.html. Python is included as a core plugin. Not only can Mise be used for Python, but also for Go, Node.js, and other languages and runtimes.
mise
respects .python-version
and .tool-versions
files that may already be in your team’s repo. It also introduces .mise.toml
which provides more features. For example, you can use it to create a virtualenv automatically when changing to a directory (see here for more information).
Replaces:
pyenv
asdf
(its closest analogue)brew
PythonUsing virtual environments is important to avoid polluting your system Python with installed packages (If you are a JavaScript developer, you can think of them like node_modules
directories). The issue is ameliorated somewhat if you use mise
to manage versions, but nevertheless it is useful to have a different environment for each project. Mise also helps in that regard due to the aforementioned ability to automatically create virtual environments.
There is nothing magical about the way mise
creates virtual environments. You can use Python to create them manually: python -m venv .venv
.
Replaces:
virtualenvwrapper
pipenv
I oscillate between uv
or poetry
depending on the use case.
uv
is a is a drop-in replacement for pip
. It is much faster to install packages. It can also create virtual environments with uv venv
as a convenience. uv
may change its API surface in the future as it merges with another tool (see the blog post). Like pip
, dependencies can installed from or frozen into a requirements.txt
file.
poetry
is a little more structured. If you’re coming from the JavaScript world like me, it functions as a package manager similar to npm
. It has a pyproject.toml
file that is similar to package.json
. It also has a lock file, poetry.lock
, similar to package-lock.json
. It also interplays with virtual environments. From the documentation:
Poetry will always work isolated from your global Python installation. To achieve this, it will first check if it’s currently running inside a virtual environment. If it is, it will use it directly without creating a new one. But if it’s not, it will use one that it has already created or create a brand new one for you.
A pyproject.toml
file might look like this:
It’s possible to use poetry
and mise
together using the mise-poetry
plugin. poetry
will then manage the dependencies and the virtual environment, while mise
will manage the Python version.
poetry
+ mise
is my preferred combination in 2024 when I am working on a structured project (it’s great to be able to poetry add
something and have it update the pyproject.toml
), otherwise I will reach for uv
and mise
.
Replaces:
pip
pip-tools
ruff
(created by the same folks as uv
) can format, lint and organize imports in code. It has a VSCode extension. I also use Pylance
for type checking and analysis. You can enable typechecking in VSCode by adding the following to your .vscode/settings.json
:
Replaces:
black
pylint
isort
autopep8
pydantic
is a data validation library. It can validate data coming in, and serialize data going out. It is to Python what zod
is to TypeScript.
Replaces:
dataclasses