Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save denis-bz/9be0b85c244343cb87622085e1b7c372 to your computer and use it in GitHub Desktop.
Save denis-bz/9be0b85c244343cb87622085e1b7c372 to your computer and use it in GitHub Desktop.
from Denis-iMac ~bz/py/plot/folium/git Fri 2023-05-05 May 11:20z

How to make a Python package for other people to use

Keywords, tags: python packaging setup.py setuptools

Purpose: describe how to make a "package" of a Python program for other people to use. This introduction builds on the excellent oa-packaging-guide-preview.readthedocs.io — read that first.

Input files:

|-- my_package
|   |-- my_package.py
|   |-- __init__.py
|   `-- test
|       `-- test_my_package.py
|-- README.md

"Setup files" that describe the package:

|-- setup.py
|-- setup.cfg
|-- MANIFEST.in
|-- pyproject.toml
|-- changelog.txt
|-- LICENSE.BSD

These files are packaged, put into big single files for PyPI, by

python setup.py bdist_wheel  # make a .whl binary distribution
python setup.py sdist        # make a .tar.gz source distribution

(What's the difference between bdists and sdists ? For python, both will be mostly .py text files; sdists have additional files listed in MANIFEST.in. For c programs, a bdist may be mostly binary .o and .dylib files, an sdist .c .cpp .h . An analogy:

  • wheel: call-a-pizza, ready to eat
  • sdist: a box of raw ingredients, with a complicated recipe.)

Make a wheel, python setup.py bdist_wheel

A "wheel" is a single file my_package-...whl that contains my_package/... files plus files needed for pip install, as shown above. There may be different wheels for python 2 / python3, macOS / Linux / Windows, etc. Wheels are in .zip format, with suffix .whl instead of .zip; unzip -l my_package-...whl lists all the files inside one.

First define a couple of shell variable to use in the following commands:

export PKG=my_package
export USERSITE=~/Library/Python/3.7
    # where "pip install --user" puts packages on macOS
    # for other systems see pip install -h

1: Make a wheel:

python setup.py bdist_wheel  |  tee 24feb-bdist_wheel.log
# output: e.g. dist/$PKG-version-py3-none-any.whl

egrep -i 'warning|error' 24feb-bdist_wheel.log  # check the log
unzip -l dist/*.whl     # list the files inside

2: Install it in the directory $USERSITE, with pip:

pip install --user dist/*.whl
    # or --prefix $dir
tree $USERSITE/$PKG*    # if you have "tree"
ls -RF $USERSITE/$PKG*  # if not

3: Test the install:

cd to another directory
python or IPython
    import package
    print( package.__path__, package.__version__ )
cd $USERSITE/$PKG/test;  pytest *.py

You could mail this dist/*.whl file to a friend to try it out:
pip install --user --no-deps $PKG*.whl, see pip install -h.

Make an sdist, source distribution

A source distribution of a Python package is similar to a wheel — for simple packages. Building from source, though, may need to compile C or C++ or Fortran, and link to the right versions of 14 other packages and libraries; lots of things can go wrong.

1: Make an sdist:

python setup.py sdist  |  tee 24feb-sdist.log
# output: dist/$PKG-version.tar.gz

egrep -i 'warning|error' 24feb-sdist.log  # check the log
tar -tv -f dist/*.tar.gz  # list the files inside
    # sdists include the files listed in MANIFEST.in, bdist_wheels don't

2: Install and test it locally, with pip:

pip install --user dist/*.tar.gz
# Test the install as for a wheel

To make both together:

python setup.py bdist_wheel sdist  |  tee 24feb-wheel-sdist.log
# egrep the log, list dist/*, install locally, test the install, as above.

Next: upload to PyPi with twine

To upload dist/*.whl and dist/*.tar.gz to PyPi for anyone to pip install, see twine.
(I had trouble registering on pypi.org, got "400 Bad CSRF Token" on macOS 10.10, Firefox 70.0.1; Safari worked.)

First twine upload to test.pypi.org, then test both the wheel and the source dist from there:

# pip uninstall --user $PKG
pip install --user --only-binary :all: [--no-deps] $PKG  # wheel
# import $PKG

pip install --user --no-binary :all: [--no-deps] $PKG    # sdist

Then follow the last half of https://packaging.python.org/tutorials/packaging-projects (the first half uses setup.py only, not setup.cfg and MANIFEST.in).

Etc.

Where did pip install put the files on disk ?

echo n | pip uninstall package
# /.../python3.7/site-packages/$PKG/*
# /.../python3.7/site-packages/$PKG-2020.2.20.dist-info/*
# n: don't remove (must be a better way)

The version string, e.g. "0.1.2", is in two files here, both setup.py and $PKG/__init__.py. Single-sourcing-package-version lists 7 ways ! of putting it in one — for simple packages MTTIW, more trouble than it's worth.

For bigger / more professional projects, see virtual environments and pipenv.

See also

https://docs.python.org/3.7/installing/index.html
https://python-packaging-tutorial.readthedocs.io
https://packaging.python.org/tutorials/packaging-projects/ (uses setup.py only, not setup.cfg and MANIFEST.in)

Unfortunately

Warning: some of the python packaging info on the web is, in March 2020, outdated or misleading. Most unfortunately, the python packaging community seems to have agreed to disagree on similar-but-different files and programs.

Whenever something can be done in two ways, someone will be confused.
— B. Stroustrup

Comments welcome

Cheers
— denis-bz-py at t-online dot de
3 March, 27 July 2020

## 2013-06-04 Jun prefilter Bspline / exact-fit / M-N
https://github.com/denis-bz/interpol.git is unchanged:
intergrid/* barypol/* and test/* .
## 2020-02-20 Feb
https://github.com/denis-bz/intergrid.git: intergrid/* and test/*, dropped barypol.
Minor mods for Python3.
BSD-3 license
(I'm trying to get `pypi install [--user / --prefix <dir>] intergrid` to work,
no fun at all.)
BSD 3-Clause License https://opensource.org/licenses/BSD-3-Clause
Copyright (c) 2014-2020 Denis Bzowy
# MANIFEST.in: add these files to sdists
# see https://docs.python.org/3.7/distutils/commandref.html
include README.md
include LICENSE.BSD
include pyproject.toml
include changelog.txt
include Makefile
include my_package/test/*.py
# include my_package/test/*.log
# pyproject.toml
# from https://oa-packaging-guide-preview.readthedocs.io/en/latest/minimal.html
[build-system]
requires = ["setuptools", "wheel"] # , "setuptools_scm"]
build-backend = 'setuptools.build_meta'
# setup.cfg
# https://docs.python.org/3.7/distutils/configfile.html
[metadata]
name = minimal_package
# lower case, "_" not "-"
# version = 2020.02.20 # in setup.py, not here
description = Minimal files to build a Python package to distribute on PyPi
long_description = file: README.md
long_description_content_type = text/markdown,
# or rst, twine check dist/*
keywords = python packaging setup.py
author = denis
author_email = denis-bz-py@t-online.de
license = LICENSE.BSD
url = https://github.com/denis-bz/minimal_package
# classifiers =
# https://pypi.org/pypi?:action=list_classifiers
# Development Status :: 3 - Alpha
# Intended Audience :: Science/Research
# License :: OSI Approved :: BSD License
# Operating System :: OS Independent
# Programming Language :: Python :: 3.7
# Topic :: System :: Archiving :: Packaging
[options]
zip_safe = False
packages = find:
install_requires =
numpy
scipy
# python setup.py [bdist_wheel sdist] also reads
# setup.cfg
# MANIFEST.in
# pyproject.toml
from setuptools import setup
setup(
version = "2020.2.20",
include_package_data = True, # files in MANIFEST.in
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment