Publish PyPi package
March 18, 2021
Publishing PyPi packages Being in the process of publishing my second PyPi-package, I take notes on the process for quicker turn-around in the future.
I usually start my projects very barebones, with a single directory and a main.py file:
$ tree
project-name
├── main.py
├── README.md
├── requirements.txt
After something useful has been hacked together, I start moving things around.
$ tree
project-name
├── LICENSE
├── README.md
├── data
│ └── example_data.txt
├── package_name
│ ├── __init__.py
│ └── package_name.py
├── requirements.txt
├── setup.py
└── tests
As an MVP, the important thing here is to move the main.py
into a package directory and to create a setup.py
.
The setup.py
file looks something like this:
import setuptools
with open('README.md', 'r') as fh:
long_description = fh.read()
setuptools.setup(
name='project_name',
version='0.0.1',
author='Casper Lehmann',
author_email='@.com',
description='Project description',
long_description=long_description,
long_description_content_type='text/markdown',
url='https://github.com/casperlehmann/project-name',
packages=setuptools.find_packages(),
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
],
python_requires='>=3.6',
install_requires=[
'wheel',
],
)
To test the installation process, run:
$ python setup.py sdist bdist_wheel
A classic error to get on this one is:
$ python setup.py sdist bdist_wheel
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: setup.py --help [cmd1 cmd2 ...]
or: setup.py --help-commands
or: setup.py cmd --help
error: invalid command 'bdist_wheel'
The problem here is that the wheel package is not installed. Pip install it in your venv, or globally, if you like to YOLO.
$ pip install wheel
Collecting wheel
Using cached wheel-0.36.2-py2.py3-none-any.whl (35 kB)
Installing collected packages: wheel
Successfully installed wheel-0.36.2
Run setup.py
again, and you’ll get a dist
-directory in your project. This contains all that needs to be uploaded.
Register with PyPi
https://pypi.org/account/register/
Prepare
$ pip install twine
Test
Upload to TestPyPi
$ twine upload --repository testpypi dist/*
Pip install with no-deps, since we cannot guarantee that TestPyPi has the same packages available as PyPi.
Note: This example uses —index-url flag to specify TestPyPI instead of live PyPI. Additionally, it specifies —no-deps. Since TestPyPI doesn’t have the same packages as the live PyPI, it’s possible that attempting to install dependencies may fail or install something unexpected. While our example package doesn’t have any dependencies, it’s a good practice to avoid installing dependencies when using TestPyPI.
https://readthedocs.org/projects/python-packaging-user-guide/downloads/pdf/latest/, p. 25
Try it in a new venv.
$ python -m venv .venv
$ pip install --no-deps -i https://test.pypi.org/simple/ project-name==0.0.1
$ python -m project_name
The last command will fail if there are missing packages, but that’s OK as long as our package installed.
Publish
$ twine upload dist/*
Install
$ pip install project_name
Adding command line script
Two steps need to be completed to install a package as a command line on your system.
Write a script that handles arguments, etc. Then specify the inclusion of this script in setup.py
.
In setup.py
add the following:
setuptools.setup(
...
scripts = ['bin/script-name'],
)
Then create the file bin/script-name
(no extension required), and write something like the following:
#!/usr/bin/env python
import argparse
from project_name import function_name
parser = argparse.ArgumentParser(description='Describe the functionality')
parser.add_argument('Path',
metavar='path',
type=str,
help='the path to some file')
args = parser.parse_args()
function_name.convert(args.Path)