Search code examples
pythondistutilspypi

Default server in multiple server configuration of distutils in ~/.pypirc


I want to have multiple PyPI servers in my ~/.pypirc file so I can easily publish to different servers, depending on the project.

My use-case is this, I have some internal projects that I want to publish to an internal PyPI server (https://pypi.internal), and I have some public projects that I want to publish to the public PyPI.

This is my current attempt, but it doesn't work. I want to default to internal, and be required to add the -r pypi (to the setup.py command) if I want to publish to the public server.

[distutils]
index-servers =
    internal
    pypi

[internal]
repository: https://pypi.internal
username: brad

[pypi]
username: brad

Where am I going wrong?


Solution

  • It's strange that there isn't built-in support for setting a default, but here are two options which may help you work around it.

    Option 1: Probably the simplest solution would be to leave your ~/.pypirc script intact and create shell aliases for your internal and public uploads. This may give you more control over customizing things for your workflow.

    Given this .pypirc file:

    [distutils]
    index-servers =
        pypi
        internal
    
    [pypi]
    repository: http://pypi.python.org/pypi
    username: brad
    password: <pass>
    
    [internal]
    repository: http://localhost:8080
    username: brad
    password: <pass>
    

    Create some shell aliases (place these definitions in your shell's rcfile, e.g. ~/.bashrc):

    alias ppup_internal='python setup.py bdist_egg sdist upload -r internal'
    alias ppup_public='python setup.py bdist_egg sdist upload'
    

    Usage:

    % ppup_internal
    ...
    running upload
    Submitting dist/foo-0.0.0.tar.gz to http://localhost:8080
    Server response (200): OK
    

    Option 2: A hack: you can work around the default by patching the default repository name at the top of your setup.py scripts.

    from distutils import config
    config.PyPIRCCommand.DEFAULT_REPOSITORY = 'internal'
    from setuptools import setup
    
    setup(
        name='foo',
        ...
    

    Output:

    % python setup.py sdist upload 
    ...
    running upload
    Submitting dist/foo-0.0.0.tar.gz to http://localhost:8080
    Server response (200): OK
    
    % python setup.py sdist upload -r pypi
    ...
    running upload
    Submitting dist/foo-0.0.0.tar.gz to http://pypi.python.org/pypi
    Server response (200): OK
    

    Background: If you define the [distutils] key in .pypirc, the upload command defaults to the pypi url when the -r [repo] argument is omitted. The relevant code is in distutils.config.PyPIRCCommand:

    class PyPIRCCommand(Command):
    
        DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi'
    
        def _read_pypirc(self):
            if os.path.exists(rc):
                self.announce('Using PyPI login from %s' % rc)
                repository = self.repository or self.DEFAULT_REPOSITORY
                realm = self.realm or self.DEFAULT_REALM
    

    The old format of .pypirc expected a [server-login] section, which was far less flexible since it only defines a single target repository. This isn't a workable option since the [pypi] section below will be unusable:

    [server-login]
    repository: http://localhost:8080
    username: brad
    password: <pass>
    
    [pypi]
    repository: http://pypi.python.org/pypi
    username: brad
    password: <pass>
    

    Now by default distutils will use this target:

    % python setup.py sdist upload
    ...
    running upload
    Submitting dist/foo-0.0.0.tar.gz to http://localhost:8080
    Server response (200): OK    
    

    But you can't access the any other repos: it silently defaults to the [server-login] properties:

    % python setup.py sdist upload -r pypi
    ...
    running upload
    Submitting dist/foo-0.0.0.tar.gz to http://localhost:8080
    Server response (200): OK