Prevent Twine From Uploading to Public PyPi

I’d like to host some private Python packages on a private PyPi server. I understand that using the —index-url option can be used to tell twine to upload to the private URL, as opposed to the default public PyPi server.

But what if a user forgets to specify the private url? Wouldn’t the package then be uploaded and registered on PyPi? This feels like a pretty easy mistake to make.

What’s the current recommended workflow for managing a private PyPi server alongside the default public server? Are there any safeguards I can use to stop a user from accidentally uploading private code to a public repository?

Apologies for the simple question. I can return to StackOverflow if that’s a better setting for this. Y’all are just way nicer :slight_smile:

I think you can use the configuration file to support that use case: The .pypirc file — Python Packaging User Guide

(If you really want to be paranoid about preventing accidental uploads, I suppose you can assign an invalid URL to pypi and then create a real-pypi repository that can be explicitly specified with twine’s --repository option).

1 Like

Yeah I think I do want to be paranoid about accidental uploads. My understand about those config options are if the first repo fails (say the private server is down) then it will try the next one.

I was hoping for a solution other than breaking regular PyPi for all users, but sounds like that’s maybe the best option for now.

I think you can also export a TWINE_REPOSITORY environment variable, but excluding pypi in the configuration file seems like the safest choice.

1 Like

Another thing you can do is give all your packages an invalid Trove classifier that your private repo accepts (something like “Private :: Keep Off PyPI”); that way, any uploads that are made to the public PyPI will be rejected.


You can set /etc/hosts or run a DNS server directing
to or similar, or set up a fire wall.

1 Like

This is correct, we explicitly prohibit any classifier that starts with Private :: at the database-level, but any ‘invalid’ classifier will probably do.


There’s a bunch of options. All of these involve you placing some sort of safeguards to prevent this from happening.

If you want to prevent this on a per-package basis, you could be to have a classifier set on the packages, that starts with Private ::. PyPI will always reject those.

If you want to prevent this on a per-machind level, you could configure the tooling to upload to a different location.

Or you could set up a network block to preventing pushing code to it.

There’s various answers here, and you’ll have to pick which fits your needs.

1 Like

Private :: is a neat trick! Is this documented somewhere? Unfortunately, it’s proving hard to Google.

1 Like

Not yet! Document the "Do not upload" classifier trick · Issue #643 · pypa/ · GitHub


What about registering the project names on the public PyPI, and not giving the credentials to anyone? This would work well in case you have a definite list of project names. There is also the added benefit of avoiding clashes when two projects of the same name exist on public and private repositories, which might cause issues during dependency resolution.

But I am not sure what is the current etiquette regarding name squatting on PyPI.

It feels like it would be nice if one could reserve a full namespace on PyPI for their own username and/or organization name (for example all sinoroc.* names on PyPI for me, and by convention, I would kind of have loose ownership of the whole sinoroc.* namespace of importable packages). By the way I am not clear if it is something that will be worked on in the “organization account” feature, not clear at all.

Namesquatting is not permitted on PyPI. While we don’t have the bandwidth to remove projects to blanket enforce this, we can (and have, as far as I know) removed namesquatting projects for a variety of reasons; including the fact that someone pointed out that they wanted to use a namesquatted name.


To note, PyPI does namesquat Fedora distro Python packages that have been not published to PyPI, though that is somewhat different than individuals or corporations doing so simply to prevent their own internal users from violating their own internal policy.

Those aren’t namesquatted. They’ve been added to a list of prohibited names, for reasons discussed in that thread.

Now documented at Classifiers · PyPI and Packaging and distributing projects — Python Packaging User Guide.