Last update on .

Should you ever work on a Python project to be consumed by the general public, sooner or later you will need to make it available on the Python Package Index (also known as PyPI). And, if you follow the Python Packaging Guide, you will learn about the tool to use for this purpose - Twine. Following the same thread, you might find out about the Test PyPI instance, which would seem like a perfect spot to try out your release on before making it available on the main PyPI repository. Except for one big gotcha...

So, as I am hacking away some final bits and pieces for my Gimmecert project, I have decided to take out as much tedium as possible from my release process. Given my previous experiences, I figured I might as well as try to write a convenient script that will take care of preparing the Git repository, including some release commits, maintenance branching, etc.

One of the parts of this process is publishing the new releases to PyPI.

In order not to mess-up my main project on PyPI, I decided to use the test instance for this purpose. After some scripting, I got the publishing process to work as desired, but then I ran into a small issue - PyPI does not allow re-upload of a same package version (unless package hashes are identical). While this might be actually a very good idea for the main PyPI archive, in order to ensure better integrity of packages, the problem is that the same behaviour is applied against the Test PyPI.

This, in turn, means that you will find it rather hard/annoying to develop release scripts like the one I am working on, or you won't be able to actually test the upload process prior to pushing it to main PyPI. Why do I say this? Well, if you think about it, you try to push to the Test PyPI, and figure out you've messed something up. After that, once you make the necessary fixes, you won't be able to push that exact same version there again. You will need to make a new release, with updated version. Which kind of beats the purpose of using the Test PyPI as a pre-check for the main upload. You keep bumping the version all the time in case of mistakes.

This, of course, is something that has been known to a number of people already, and there is a somewhat lengthy discussion on Github about it.

Now... If you have the patience to read through that thread of comments, you will find out about a nice project called devpi. And this is what came to my rescue - at least for release script testing.

In essence, the devpi project allows you to run your own local PyPI-compatible server. Now, it does come with a bunch of nice features, and it can be used as caching server too, but that's not the main focus for this post. My main idea is to use it as upload test-bed.

If you are looking for a nice getting started guide, I would whole-heartedly recommend Stefan Scherfke's post on this topic. I'll just outline a very quick-and-dirty set of instructions to test out your upload.

Start off by creating a separate virtual environment and installing devpi (you are using Python 3 nowadays, right? :):

mkvirtualenv -p /usr/bin/python3 devpi
workon devpi
pip install devpi

Create a temporary directory where the devpi files will be stored at (you could also skip this and let it use the default ~/.devpi location):

mkdir /tmp/devpi

Now start and initialise the server (the --no-root-pypi option prevents it from fetching package information from which can save some processing/bandwidth):

devpi-server --serverdir /tmp/devpi --start --init --no-root-pypi

The server will be started in the background. The server will by default be available under http://localhost:3141/ (it binds to loopback interface by default). When you want to stop it, simply run:

devpi-server --serverdir /tmp/devpi --stop

If you need to start it again, simply skip the --init option:

devpi-server --serverdir /tmp/devpi --start

Configure the devpi client (used to configure the server) to use the local instance:

devpi use http://localhost:3141/

Create a user for testing the uploads:

devpi user -c myuser


devpi login myuser

Create an index (repository) for testing uploads (the volatile option will allow you to overwrite existing package versions, which is useful for this type of testing):

devpi index -c myuser/testupload volatile=True

At this point you should have a fully working local PyPI server. To upload things to it, simply go to the root of your project (possibly switching to correct Python virtual environment):

workon myproject
python sdist bdist_wheel
twine register --username myuser --repository myuser/testupload --repository-url http://localhost:3141/myuser/testupload/ dist/*.tar.gz
twine upload --username myuser --repository-url http://localhost:3141/myuser/testupload/ dist/*

And, as a final touch, just open up your local PyPI repository URL and see if everything looks fine.

Once you are done with testing, you can simply stop the devpi server, and remove the directory /tmp/devpi.

Happy hacking.


Pingbacks are closed.


Comments are closed.