Skip to content

Instantly share code, notes, and snippets.

@aesuli
Last active May 19, 2021 14:09
Show Gist options
  • Save aesuli/b7c18605268ae812d1cb4ac54982bb74 to your computer and use it in GitHub Desktop.
Save aesuli/b7c18605268ae812d1cb4ac54982bb74 to your computer and use it in GitHub Desktop.
A step-by-step guide on how to create a pip package

Pip packaging

Note: This guide is tailored on my preferences, e.g., I use conda.

Prerequisites

Reference: https://packaging.python.org/guides/distributing-packages-using-setuptools/#create-an-account

Create an account here: https://pypi.org/
Create API tokens here: https://pypi.org/manage/account/
and copy them in the .pypirc file in the $HOME or %userprofile% directory, e.g.:

[pypi]
  username = __token__
  password = pypi-*******************************

Reference: https://packaging.python.org/guides/distributing-packages-using-setuptools/#requirements-for-packaging-and-distributing

I need an environment:

conda create -n mypipenv python pip setuptools wheel twine
conda activate mypipenv

Package setup

Reference: https://packaging.python.org/guides/distributing-packages-using-setuptools/#configuring-your-project

Take the template setup.py file from https://github.com/pypa/sampleproject/blob/main/setup.py and customize it.

I like to automatically get the version number from the init.py file of the package:

def get_version(rel_path):
    init_content = (here / rel_path).read_text(encoding='utf-8')
    for line in init_content.split('\n'):
        if line.startswith('__version__'):
            delim = '"' if '"' in line else "'"
            return line.split(delim)[1]
    else:
        raise RuntimeError("Unable to find version string.")

then set version=get_version("twiget/__init__.py"), in the setup(...) call

Along with names, authors and the like, remember to:

  • create a README.md file, for the long description
  • set the license file, which should be called COPYING
  • set the classifiers (status, audience, license...)
  • set the requirements
  • set the package list
  • set the py_modules list (i.e., single py files not in a package, e.g., scripts)
  • set any eventual console script, using the entry_points argument:
        entry_points={
          'console_scripts': [
              'twiget-cli=twiget_cli:main',
          ],
        },

Building the package

Reference: https://packaging.python.org/guides/distributing-packages-using-setuptools/#packaging-your-project

A package file is called wheel.
Wheels may contain source code (eventually built on the fly when installed), or binary code (precompiled).
When working with pure python (like I do 99.99% of the time) there is no key difference among the two.

To build a wheel, cd to the root directory of the software package.

Source wheel build command:

python setup.py sdist

Binary wheel (pure python) build command:

python setup.py bdist_wheel

The build process puts the prepared packages in the build directory.

Testing the packages

Packages can be checked with the command.

twine check dist/*

Also, I create a test enviroment and install the package:

pip install dist/file.whl --force-reinstall

The I test if the code is working as expected.

Publishing to the python package index (PyPI)

Reference: https://packaging.python.org/guides/distributing-packages-using-setuptools/#uploading-your-project-to-pypi

Once the packages are built and they pass the check, they can be uploaded to pypi:

twine upload dist/*

Wait a minute and the package is available for install using the command:

pip install package_name

New versions

Whenever the package is updated, the version number must be increased.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment