Close modal

Blog Post

Using a private pypi repository with pypiserver (and alternatives)

Development
Wed 23 May 2018
0 Comments


Some options:

  • Create your own PyPi server
  • Use Dumb PyPi to generate and farm out the context to a simple web server.
  • Some other options such as PyPiCloud which falls in between the two above.

Using PyPiServer

Depending on your workflow, and how frequently and many packages are updated, PyPi server may be the most suitable, it does require an application server (python/bottle) to function, but does not require the whole thing to be rebuilt and pushed as the static version, and can handle authentication for actions (configurable).

For this case, to make things even easier, we will use the PyPiServer docker image to get going even faster, if you do not use Docker then the full installation can be achieved, or use of the static solutions mentioned.

Installation

On your Docker host extract or checkout the docker project for PyPiServer.

$ mkdir /srv/pypi
$ printf "USER:$(openssl passwd -crypt PASSWORD)\n" >> .htpasswd
$ docker run -t -i --rm \
    -h pypi.mydomain.com \
    -v /srv/pypi:/srv/pypi:rw \
    -p <some_external_port>:80 \
    --name pypi \
    codekoala/pypi

You should now have a pypi server accessible on localhost:. Use a reverse proxy or the like to expose this if you prefer, and access pypi.mydomain.com.

Uploading to your server

The standard layour for any package to upload to PyPi looks like:

  • README.md
  • my_package
  • __init__.py
  • some_utility.py
  • setup.cfg
  • setup.py

The setup.cfg file is rather simple and looks like so:

[metadata]
description-file = README.md

As for setup.py, it will look like:

from distutils.core import setup
setup(
  name = 'my_package',
  packages = ['my_package'], # this must be the same as the name above
  install_requires=[],  # list any dependencies that must be installed to use this
  version = '0.0.1',
  description = 'My useful python library',
  author = 'Your Name Here',
  author_email = '[email protected]',
  url = 'https://gitlab.com/your_name/your_repo', # Where the repo is
  download_url = 'https://gitlab.com/your_name/your_repo/some_archive.tar.gz', # Archive you can curl
  keywords = ['utilities'], # any useful keywords
  classifiers = []
)

Obviously __init__.py will expose the classes like:

from some_utility import Widget

Also, that is assuming some_utility.py looks like :

class Widget:
    def __init__(self):
    pass

One more thing, edit your ~/.pypirc :::ini [server-login] repository: https://pypi.mydomain.com username: USER password: PASSWORD

Now we can upload!

$ python3 setup.py sdist upload -r https://pypi.mydomain.com
Using pip to install

There are a couple of ways to do this:

Command line options

When you invoke pip, you can specify the private server as per below:

$ pip3 install my_package--trusted-host pypi.mydomain.com --index-url https://pypi.mydomain.com
Profile

Edit your ~/.bashrc or ~/.zshrc etc

export PIP_EXTRA_INDEX_URL=http://pypi.mydomain.com/simple/
PIP config

Edit ~/.pip/pip.conf

[global]
extra-index-url = http://pypi.mydomain.com/simple/

That's it, fire it up and you should see it go. You can also install packages that are on the regular pypi even if you have specified your private server, however I imagine conflicting names would install first from the private repo.

Using Dumb PyPi, etc

I could not get Dumb PyPi to work due to an import error with pip.wheel (not sure if a versioning issue, so I will not cover it in detail.

The layout of the python package is the same as the above when using the PyPiServer, as well as the client side installation using pip.

So the generation of the static html and upload to a webserver (and subsequent location of web content) will be what is different, depending on your needs.

Conclusion

Using a custom/private PyPi to distribute your python packages is fairly straight forward. It is cleaner and more elegant than using things such as git submodules and allows the packages to be delivered to almost any python installation.