Lately I've been using the Azure DevOps platform for code repositories, issue tracking, wikis, CI/CD and for artifact hosting. It's a quite nice platform, the various tools are very well integrated with each other and they have nice integration with VS Code for example.

But I have one problem that keeps recurring: connecting to the private Python package repository, to which we push some of our internal libraries.

I want to have access to those packages locally, on my development machine, while I'm working on other projects that depend on those packages. The recommended way of authenticating is to use the artifacts-keyring package, which should initiate a web browser based authentication flow. For some reason, it doesn't work for me. It prompts for a username and a password in the CLI and then it says authentication failed. To be fair, it's still in preview and has been for quite some time now, so some bugs are to be expected.  

However, there is an alternate, older, authentication mechanism, but which has . For this you have to create a Personal Access Token, by clicking on the circled icon on any DevOps page and then going to the Personal Access Token page.  

On this page, click the New Token button. Fill out the fields and make sure you check one of the options under Packaging. If you only need read access (so you just want to download packages from the repository), then check only the Read box, otherwise, if you want to upload packages to Twine from the local machine, then check one of the other boxes.

Be careful, the PAT is shown only once and then you can't see it again in the UI, so make sure to copy it right away.

Now that you have the PAT, go to the Artefacts page in Azure DevOps and go to the Python repository you want to connect to. Click on the "Connect to feed" button and then on Pip and you'll find the URL to the custom repository.

Copy that thing and copy it into a pip.ini (on Windows) or pip.conf (on Linux) at the root of your virtual environment folder. These files don't exist unless you've customized something about your virtual environment, so most likely you will have to create them.

Now we have to modify that URL as follow:


Now time to test it out by trying to install something using pip. Pip should already showing that it's searching in the new index, instead of the public PyPI.

Keep in mind that these PATs have a limited duration. By default it expires after 30 days, but this can be extended up to 90 days from the dropdown form and 1 year from the date picker. Because I was lazy, I always picked the 90 days. After 90 days, when I would need a new version of one of our libraries, pip installs would start failing. Because three months had passed, I had usually forgotten how I set up everything, so I would have to go and figure it out again. I went through this 3 times already, the last time being today, so now I've decided to document it for myself somewhere I can find it easily.

I’m publishing this as part of 100 Days To Offload - Day 2.