Using devpi as an offline PyPI cache

Want to develop Python applications, but realize the dependence on the network and PyPI is a hindrance? devpi provides a robust way to keep a cache of all the packages you need to develop. And it’s super-easy.

Install devpi

First, install devpi as an application somewhere on your system. pipx provides a great way to do that:


~ $ pipx install devpi-server
  installed package devpi-server 6.1.0, Python 3.9.6
  These apps are now globally available
    - devpi-export
    - devpi-fsck
    - devpi-gen-config
    - devpi-gen-secret
    - devpi-import
    - devpi-init
    - devpi-passwd
    - devpi-server
done! ✨ 🌟 ✨

~ $ devpi-init
INFO  NOCTX Loading node info from /Users/jaraco/.devpi/server/.nodeinfo
INFO  NOCTX generated uuid: 63c633d39fb545f08f1432172c5ee4eb
INFO  NOCTX wrote nodeinfo to: /Users/jaraco/.devpi/server/.nodeinfo
INFO  NOCTX DB: Creating schema
INFO  [Wtx-1] setting password for user 'root'
INFO  [Wtx-1] created user 'root'
INFO  [Wtx-1] created root user
INFO  [Wtx-1] created root/pypi index
INFO  [Wtx-1] fswriter0: commited at 0
~ $ devpi-server
2021-08-03 08:02:33,706 INFO  NOCTX Loading node info from /Users/jaraco/.devpi/server/.nodeinfo
2021-08-03 08:02:33,707 INFO  NOCTX wrote nodeinfo to: /Users/jaraco/.devpi/server/.nodeinfo
2021-08-03 08:02:33,787 WARNI NOCTX No secret file provided, creating a new random secret. Login tokens issued before are invalid. Use --secretfile option to provide a persistent secret. You can create a proper secret with the devpi-gen-secret command.
2021-08-03 08:02:36,913 INFO  NOCTX devpi-server version: 6.1.0
2021-08-03 08:02:36,913 INFO  NOCTX serverdir: /Users/jaraco/.devpi/server
2021-08-03 08:02:36,913 INFO  NOCTX uuid: 63c633d39fb545f08f1432172c5ee4eb
2021-08-03 08:02:36,913 INFO  NOCTX serving at url: http://localhost:3141 (might be http://[localhost]:3141 for IPv6)
2021-08-03 08:02:36,913 INFO  NOCTX using 50 threads
2021-08-03 08:02:36,913 INFO  NOCTX bug tracker: https://github.com/devpi/devpi/issues
2021-08-03 08:02:36,913 INFO  NOCTX IRC: #devpi on irc.freenode.net
2021-08-03 08:02:36,913 INFO  NOCTX Hit Ctrl-C to quit.
2021-08-03 08:02:36,921 INFO  Serving on http://127.0.0.1:3141
2021-08-03 08:02:36,921 INFO  Serving on http://[::1]:3141

Now a locally-running PyPI mirror is running at localhost:3141/root/pypi/!

Leave that server running in its own window or tab.

Prime the cache

Although the server is running, it does not yet have a mirror of the packages needed to build and test the project. To get those, in a separate terminal, install the relevant project(s) or run the project’s test suite using devpi as the package index:

~ $ $PIP_INDEX_URL = 'http://localhost:3141/root/pypi/'
~ $ # or in bash:
~ $ # export PIP_INDEX_URL=http://localhost:3141/root/pypi/
~ $ pip-run -q jaraco.mongodb pmxbot pip-run jupyter -- -c pass

By setting PIP_INDEX_URL, pip will now request packages from the local index, which will mirror the packages from PyPI and keep a local cache.

Although this example uses pip-run to download the packages, one could just as easily use pip or tox.

If priming the cache using tox, be sure to pass -r to tox to rebuild the environments from scratch to get all packages.

Testing the cache

Next, test that the cache is working by disabling the network and putting devpi in offline mode.

~ $ sudo ifconfig en0 down
Password:
~ $ ping pypi.org
ping: cannot resolve pypi.org: Unknown host

To restart devpi in offline mode, kill the existing process by hitting Ctrl+C in the devpi-server terminal and re-launch with --offline-mode:

...
2021-08-03 08:10:22,861 INFO  [req365] GET /root/pypi/+f/144/75042e2849910/idna-3.2-py3-none-any.whl
2021-08-03 08:10:22,874 INFO  [req366] GET /root/pypi/certifi/
2021-08-03 08:10:22,923 INFO  [req367] GET /root/pypi/+f/50b/1e4f8446b06f4/certifi-2021.5.30-py2.py3-none-any.whl
^C~ $ devpi-server --offline-mode
2021-08-03 08:32:12,603 INFO  NOCTX Loading node info from /Users/jaraco/.devpi/server/.nodeinfo
2021-08-03 08:32:12,606 INFO  NOCTX wrote nodeinfo to: /Users/jaraco/.devpi/server/.nodeinfo
2021-08-03 08:32:12,712 WARNI NOCTX No secret file provided, creating a new random secret. Login tokens issued before are invalid. Use --secretfile option to provide a persistent secret. You can create a proper secret with the devpi-gen-secret command.
2021-08-03 08:32:16,659 INFO  NOCTX devpi-server version: 6.1.0
2021-08-03 08:32:16,659 INFO  NOCTX serverdir: /Users/jaraco/.devpi/server
2021-08-03 08:32:16,660 INFO  NOCTX uuid: 63c633d39fb545f08f1432172c5ee4eb
2021-08-03 08:32:16,660 INFO  NOCTX serving at url: http://localhost:3141 (might be http://[localhost]:3141 for IPv6)
2021-08-03 08:32:16,660 INFO  NOCTX using 50 threads
2021-08-03 08:32:16,661 INFO  NOCTX bug tracker: https://github.com/devpi/devpi/issues
2021-08-03 08:32:16,661 INFO  NOCTX IRC: #devpi on irc.freenode.net
2021-08-03 08:32:16,661 INFO  NOCTX Hit Ctrl-C to quit.
2021-08-03 08:32:16,673 INFO  Serving on http://127.0.0.1:3141
2021-08-03 08:32:16,673 INFO  Serving on http://[::1]:3141

Now observe that the install commands that ran previously will succeed entirely without network access:

~ $ pip-run -q jaraco.mongodb pmxbot pip-run jupyter -- -c "import jupyter; print('worked!')"
worked!

Beyond a mirror

Devpi can do so much more, including providing development indexes to store non-public packages or packages under development (while still mirroring upstream repos).

It also provides for repository replication, allowing your team to share a devpi instance with public and private packages and then developers can sync a replica of that instance with all the public and private packages used by the team.

Be sure to check out https://devpi.net for more details.

Written on August 3, 2021