Boilerplate is not your friend
When someone creates a new Python project, often they are implementing the same boilerplate as thousands of other projects. Django is probably the best example of this. Understandably, the official Django docs provide its users a command to initialize a project:
$ django-admin startproject mysite djangotutorial
This command, presumes that you either have a virtual environment activated, or that Django is globally installed. Of course, as everyone hopefully knows nowadays, you should never create a project without using a virtual environment (unless you have good reason).
While Django does explain this somewhat on its installation guide, it still takes for granted that you need a system installation of Python. That means that anyone who is new to Python, and is following the documentation closely, will miss out on the benefits of uv. For more on uv and why it is important, read this article.
Perhaps people assume that packaging tools are the preoccupation of advanced users. If that is the case, people learning Python from scratch will have a needlessly convoluted introduction to packaging and project management, all because they were steered in the wrong direction.
Ultimately, there are numerous steps to create a Django project:
- Install Python if not already installed.
- Create a directory in which to create the project.
- Create a virtual Python environment.
- Activate the virtual environment.
- Install Django with
pip
. - Intialize the Django project with the previously shown command.
Not only is it slow, it also means that someone new to Python has more possible points of failure. And while failure can be a good learning experience, it’s not worth it if we are teaching people outdated project management techniques. The truth is that we are able to shorten this process.
- Install uv if not already installed.
- Use uv to initialize a project.
- Install Django with uv.
- Intialize the Django project with the previously shown command.
That’s not bad. But it could be even better.
Trimming the fat
While Django is a good example, it is not the only Python web framework that involves a tedious process to simply create a project. Pelican has a similar process. For those who don’t know, Pelican is a static-site generator written in Python. Much like Django, it has its own special command to initialize a Pelican project (again, you will be needed to have a Python virtual environment made ready prior):
$ pelican-quickstart
When teaching someone to program, there are often competing goals of teaching something so simple that they understand it, while also teaching them something so useful that they benefit from using it in the future. I wanted the best of both worlds. The reason I first touched Pelican was because I wanted to show someone completely new to programming how to create a GitHub Pages static-site in just minutes, using a Python-based static site generator. Pelican is presently the most popular tool for the job.
For reasons that I hope readers can understand, using uv isn’t simply superior to a vanilla packaging experience. It’s easier. Teaching good packaging practices should frankly be taught as early as possible.
Of course, I could go through the following steps:
- Install uv if not already installed.
- Use uv to initialize a project.
- Install Pelican with uv.
- Intialize the Pelican project with the previously shown command.
But like I said, there’s an even easier way to do this, inspired by a
packaging system for a completely different language. You see, for all the
many, many flaws of Node.js, it is very easy to start a project by using
the npx
command. npx
, which stands for “Node Package eXecuter”, can
download and install an npm package, then run a command from that package. For
example:
$ npx cowsay "Visit elliotsimpson.org!"
__________________________
< Visit elliotsimpson.org! >
--------------------------
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
There’s a little more to the command, but this is the core functionality that
makes it so good. You don’t need to install cowsay
before running it, and you
don’t need to worry about the effect it will have on your system, because it
gets cleaned up for you.
Besides making cows give dubious advice, npx
is commonly recommended for the
initialization of new projects. If you want to create a Next.js application,
you can type npx create-next-app
. If you want to create a React Router
application, you can type npx create-vite
. These tools can create your
project folder, populate it with a template, and install all required
dependencies, without any trouble.
Turbopelican
You can likely see where all this is going. Thus we have the uvx
command. Of
course, some users may already be familiar with pipx
, which was very similar,
but didn’t fit into the uv ecosystem. So if one installs uv, it becomes
possible to do the following:
$ mkdir my-website
$ cd my-website
$ uvx --from pelican pelican-quickstart
It’s a little verbose, but it gets the job done. Or at least, some of the job.
The pelican-quickstart
command simply creates a new project via a template,
but it doesn’t actually set up an environment. It rather leaves that to the
user, especially because the way the docs are written, people will install
Pelican into their virtual environment (and thus the project’s virtual
environment becomes a prerequisite to run the command in the first place).
I wasn’t particularly happy with this. If create-next-app
can be
batteries-included, why can’t Pelican? Of course, it is unlikely that Pelican
will implement functionality reliant on uv for the indefinite future, but we
can take matters into our own hands.
And so I devised a plan. I would create my own Python package which could
create a Pelican project from scratch, without the need for any existing
virtual environment. This tool I would call
Turbopelican. The package has
the same name as its command (turbopelican
of course), such that when I run
uvx
, I don’t need to specify a command separately from the package name
– it correctly assumes that the turbopelican
command is from the
turbopelican
package. This command copies a bunch of files into the target
directory. Also, because I wanted in particular for the user to benefit from
GitHub Pages, it copies across a workflow to make Pelican generate a static
site and deploy it. Also, importantly, it uses uv to create a virtual
environment for the project.
If you have uv installed you can run it with the following command:
$ uvx turbopelican init path/to/projects/folder/my-website-name
Notice how what was once quite a long list of tasks has turned into the following:
- Install uv if not already installed.
- Run turbopelican.
By only supporting use of my tool with the uvx
command, I also eliminate the
scenario in which a user initializes a new project without having uv.
Therefore, I can guarantee that the user receives a uv-managed virtual
environment.
As many of you could likely see coming, feature creep attacked before I even put out a single release, and I soon wanted a more dynamic configuration loading system… but that is another story. The main thing is that the proof-of-concept worked, was very easy to implement, and was very, very easy to use.
Conclusion
Pelican is just one of the various significant frameworks that Python has to
offer – there’s potential for other similar packages to streamline
project creation, making use of the uvx
command. Maybe soon we’ll have
commands like uvx new-django-project
or uvx pygame-init
that create not
only a template, but an entire project, complete with a virtual environment
managed with uv.
Python