Skip to content

Latest commit

 

History

History
205 lines (140 loc) · 4.61 KB

File metadata and controls

205 lines (140 loc) · 4.61 KB

Packaging

Let's look at the structure of creating an installable python package.

The python packaging system is constantly evolving, and the current recommendations
of tools is list here: https://packaging.python.org/en/latest/guides/tool-recommendations/

xkcd comic on python packaging chaos

(from https://xkcd.com)

Our example

We'll work on an example that builds on the Mandelbrot set exercise from our matplotlib discussion. Our example is hosted here:

https://github.com/sbu-python-class/mymodule

On your local computer, if you have git installed, you can clone this via:

git clone https://github.com/sbu-python-class/mymodule.git

The directory structure appears as:

mymodule/
├── mymodule
│   ├── __init__.py
│   └── mandel.py
├── pyproject.toml
└── README.md

This is a rather common way of structuring a project:

  • The top-level mymodule directory is not part of the python package, but instead is where the source control (e.g. git) begins, and also hosts setup files that are used for installation

  • mymodule/mymodule is the actual python module that we will load.

    To make python recognize this as a module, we need an `__init__.py`
    file there—it can be completely empty.
    
  • The actual *.py files that make up our module are in mymodule/mymodule

Right now, this package does not appear in our python search path, so the only way to load it is to work in the top-level mymodule/ directory, and then we can do:

import mymodule.mandel

we could also do:

from mymodule.mandel import mandelbrot

setuptools

A popular set of packages are:

  • Installation:

    • pip to install packages from PyPI
    • conda for disctribution cross-platform software stacks
  • Packaging tools:

    • setuptools to create source distributions
    • build for binary distributions
    • twine to upload to PyPI

We'll look at how to use setuptools to package our library.

A lot of setuptools documentation is out-of-date and
inconsistent with the packaging guidelines.

Packages used to create a `setup.py` file that had all of the project information,
but this is deprecated.  Instead we should create a
[pyproject.toml](https://packaging.python.org/en/latest/guides/writing-pyproject-toml/) file---this
is consistent with [PEP 517](https://peps.python.org/pep-0517/).

Here's a first pyproject.toml:

[build-system]
requires = ["setuptools"]
build-backend = "setuptools.build_meta"

[project]
name = "mymodule"
description = "test module for PHY 546"
readme = "README.md"
license.text = "BSD"
version="0.1.0"
authors = [
  {name="Michael Zingale"},
  {email="michael.zingale@stonybrook.edu"},
]

dependencies = [
  "numpy",
  "matplotlib",
]

Some notes:

  • We have a [build-system] table that specifies the build tool. Here we choose setuptools.

  • We have a lot of metadata for our project defined in the [project] table.

  • We also list the dependencies of our project in the [project] table. This will allow the installer to install any missing packages that are required.

There are many additional options to specify how to find files that are part of the project as well as data files, etc.

`pyproject.toml` also allows you to specify defaults for tools, like
`pylint`, `flake8`, and others with a `[tool.X]` subtable.
Some projects also contain a `setup.cfg`
file when using `setuptools`.  This is
usually not needed, since we can put everything
in the `[project]` table.

Installing

We can now install simply as:

pip install .
Look in your `.local/lib/python3.12/site-packages` directory, and you'll
see the module there.

If instead, we want to install in a way that still allows us to edit the source, we can install as "editable" via:

pip install -e .

To uninstall, we can do:

pip uninstall mymodule

in a directory outside of our project (otherwise, pip may get confused).

Using our module

Once the module is installed, we can use it from any directory. For example, if we do:

import mymodule
print(mymodule.__file__)

it shows us where the module is installed on our system. In my case, it is:

/home/zingale/.local/lib/python3.14/site-packages/mymodule/__init__.py

Let's generate a plot:

from mymodule.mandel import mandelbrot
fig = mandelbrot(128)
fig.savefig("test.png")

This produces the plot shown below:

sample Mandelbrot set image using 128x128 points