Pipenv to UV

Posted on 12 April 2025 in Technology

I have been (mostly) happily using Pipenv for many years now on all of my Python projects. Recently, I heard about UV so I'm giving it a try with the very small set of Python dependencies used for this blog.

Installation

I'm also moving to using a devcontainer for this blog's dependencies so for installation I'm just using the devcontainer UV feature. Then I install the Python version I want to use (3.12 here):

0
1
2
uv python install 3.12
Installed Python 3.12.10 in 5.25s
 + cpython-3.12.10-linux-x86_64-gnu

And initial my project:

0
1
uv init
Initialized project `chris-wells.net`

This adds a .python-version file to the repo to specify the default Python version, a main.py (which is this case I delete), a pyproject.toml file with project information, and an empty README.md.

Then, I instally my short list of requirements currently in the Pipfile.

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
$ uv add pelican markdown pelican-youtube
Using CPython 3.12.10 interpreter at: /usr/local/bin/python3.12
Creating virtual environment at: .venv
Resolved 24 packages in 564ms
Prepared 22 packages in 4.34s
░░░░░░░░░░░░░░░░░░░░ [0/22] Installing wheels...

warning: Failed to hardlink files; falling back to full copy. This may lead to degraded performance. If the cache and target directories are on different filesystems, hardlinking may not be supported. If this is intentional, set `export UV_LINK_MODE=copy` or use `--link-mode=copy` to suppress this warning.

Installed 22 packages in 72ms
+ anyio==4.9.0
+ blinker==1.9.0
+ docutils==0.21.2
+ feedgenerator==2.1.0
+ idna==3.10
+ jinja2==3.1.6
+ markdown==3.8
+ markdown-it-py==3.0.0
+ markupsafe==3.0.2
+ mdurl==0.1.2
+ ordered-set==4.1.0
+ pelican==4.11.0
+ pelican-youtube==0.3.0
+ pygments==2.18.0
+ python-dateutil==2.9.0.post0
+ pytz==2025.2
+ rich==14.0.0
+ six==1.17.0
+ sniffio==1.3.1
+ typing-extensions==4.13.2
+ unidecode==1.3.8
+ watchfiles==1.0.5

This command installs the requirements and their dependencies, updates the pyproject.toml file, and adds a uv.lock file to lock the specific dependency versions.

Since speed is a big focus of UV the warning message interested me. This is a teeny project and the performance of UV isn't relevant, but I figured it was worth learning about. After following the documentation at Using uv in Docker I ended up adding some configuration to my DevContainer Dockerfile:

0
1
2
3
4
5
6
7
8
# Install UV
# See: https://docs.astral.sh/uv/guides/integration/docker/#installing-uv
# See: https://docs.astral.sh/uv/guides/integration/docker/#caching
ADD https://astral.sh/uv/install.sh /uv-installer.sh
ENV UV_INSTALL_DIR="/home/$USERNAME/.local/bin/"
RUN sh /uv-installer.sh && rm /uv-installer.sh
ENV PATH="/home/$USERNAME/.local/bin/:$PATH"
ENV UV_LINK_MODE=copy
RUN --mount=type=cache,target="/home/$USERNAME/.cache/uv"

This change tells UV to use the less efficient "copy" link mode but it does so on a Docker cache volume to improve speed.

Running Code

Now that UV is installed and configured I'll try running something:

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
uv run pelican
[16:40:54] ERROR    Cannot load plugin `pelican_youtube`
                    Cannot import plugin `pelican_youtube`
[16:40:55] ERROR    Could not process articles/2017/aoc-2017/05-days-16-20.rst
                    /workspaces/chrxs.net/content/articles/2017/aoc-2017/05-days-16-20.rst:159: (ERROR/3) Unknown directive type "youtube"

                    .. youtube:: _cZC67wXUTs

WARNING  Unable to find '05-days-16-20.rst#day-16-one-billion-permutations-in-0-535-seconds', skipping url replacement.
WARNING  Unable to find '05-days-16-20.rst#day-17-collections-deque', skipping url replacement.
WARNING  Unable to find '05-days-16-20.rst#day-18-negative-numbers-and-str-isdigit', skipping url replacement.
WARNING  Unable to find '05-days-16-20.rst#day-19-the-internet-is-not-a-big-truck', skipping url replacement.
WARNING  Other resources were not found and their urls not replaced

Done: Processed 19 articles, 1 draft, 0 hidden articles, 2 pages, 0 hidden pages and 0 draft pages in 1.11 seconds.

Mostly good! It looks like in the 2+ years since I have updated this blog and used Pelican the pelican_youtube plugin is not working.

This turns out to be because Pelican 4.5 introduced support for plugin autodiscovery so I solved this by removing the PLUGIN_PATHS and PLUGINS settings from my pelicanconf.py.

With that corrected I'm now able to get a happy build with the latest version of Pelican!

0
1
uv run pelican
Done: Processed 20 articles, 1 draft, 0 hidden articles, 2 pages, 0 hidden pages and 0 draft pages in 1.07 seconds.

And I can run the listen server also via UV:

0
1
2
3
uv run pelican --autoreload --listen
--- AutoReload Mode: Monitoring `content`, `theme` and `settings` for changes. ---
Serving site at: http://127.0.0.1:8000 - Tap CTRL-C to stop
Done: Processed 20 articles, 1 draft, 0 hidden articles, 2 pages, 0 hidden pages and 0 draft pages in 0.99 seconds.