The easiest way to publish a python package on PyPI using Poetry

Utpal Kumar   6 minute read      

In this post, we will use a new package, poetry to create and manage our pypi package. I will show you how you can create and manage a new package easily using poetry python package and then to make it available online.

The one mental model

Poetry is one tool for the whole packaging lifecycle, replacing the old patchwork of virtualenv + setup.py + twine. Everything lives in a single pyproject.toml, and four commands take you end to end:

poetry new (scaffold) → poetry add (deps) → poetry build (wheel + sdist) → poetry publish (PyPI).

Publishing a package with Poetry Poetry scaffolds a project, manages dependencies in pyproject.toml, builds a wheel and sdist, publishes to PyPI with an API token, and then anyone can pip install it. poetry new scaffold project poetry add deps → pyproject.toml poetry build wheel + sdist poetry publish to PyPI (API token) pip install anyone can use it
One tool, one config file, from empty folder to an installable package on PyPI.

Advantages of using poetry

The poetry package can do much more than the traditional environment manage such as Virtualenv. Some of them are:

  1. Relocatable: Poetry keeps the virtual enviroment separate from the project. By default, it creates the enviroment at the .cache directory at the home directory. One can easily relocate the environment to another location using the command: poetry env use <new env location>
  2. Mutable interpreter: You can also change the interpreter of your environment from, say Python 3.6 to Python 3.9 easily using the command: poetry env use python3.9
  3. Isolate the development packages: If you are writing a package for the production purpose, we need to also install extra package for the development purpose. We do not need such package for the production. Poetry allows user to separate them using the -D flag. To add a package for the development only, we can use poetry add -D package-name. Later, when we are ready for the production, we can use the no-dev flag to separate the development packages: poetry install --no-dev.
  4. Remove redundant packages: While developing a python package and playing with several features, we end up with many redundant packages. Poetry can help to easily remove such redundancies using poetry install --remove-untracked.
  5. Manage version and dependencies using pyproject.toml: The pyproject.toml is equivalent to the requirements.txt file in the traditional virtual environment manager. This stores information about the interpreter (requirements.txt file don’t), and the packages and their dependencies.

Poetry CLI has moved on since 2021 (the workflow is identical; only these names changed):

  • Dev dependencies use groups now. [tool.poetry.dev-dependencies][tool.poetry.group.dev.dependencies], and poetry add -D pkgpoetry add -G dev pkg.
  • --no-dev was removed in Poetry 2.0 → use poetry install --without dev (or --only main for just runtime deps).
  • --remove-untracked is gone → use poetry install --sync (Poetry 2.0: poetry sync).

Install the package poetry

We will first install the package poetry using pip.

pip install poetry

Better: install Poetry with pipx. Poetry is a tool, not a project dependency, so the recommended install is pipx install poetry (or the official installer script) — this isolates Poetry in its own environment instead of mixing it into your project’s.

Create a project

Let us create a project:

poetry new miniseed2mat

This will create the miniseed2mat directory with the following content:

$ tree miniseed2mat/
miniseed2mat/
├── README.rst
├── miniseed2mat
│   └── __init__.py
├── pyproject.toml
└── tests
    ├── __init__.py
    └── test_miniseed2mat.py

2 directories, 5 files

The file pyproject.toml contains:

[tool.poetry]
name = "miniseed2mat"
version = "0.1.0"
description = ""
authors = ["Utpal Kumar <utpalkumar@email.com>"]

[tool.poetry.dependencies]
python = "^3.9"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

Existing Project

If you already have an existing poetry project, you can initialize it using the command inside the prepopulated directory:

poetry init

Adding more dependencies

You can add more dependencies to your project by specifying them in the tool.poetry.dependencies section of the pyproject.toml file.

[tool.poetry.dependencies]
python = ">=3.9,<3.11"
scipy = "^1.7.3"
obspy = "^1.2.2"

Alternatively, you can install them by running the command:

poetry add scipy obspy

Add a script file

Next, we add a script file main.py in the main directory -> miniseed2mat/miniseed2mat/main.py. You can get this script from the previous post Analyzing MiniSEED seismic data in MATLAB.

We specify the entry point to Poetry by adding the following code snippet to the file pyproject.toml.

[tool.poetry.scripts]
mseed2mat = "miniseed2mat.main:convertmseed2mat"

Here, we call the function convertmseed2mat in the main.py script file.

Build the package

Now, we can build the package using the command:

$ poetry build
Building miniseed2mat (0.1.0)
  - Building sdist
  - Built miniseed2mat-0.1.0.tar.gz
  - Building wheel
  - Built miniseed2mat-0.1.0-py3-none-any.whl

This will create a dist folder inside your project with wheel and tar files of your project.

$ tree dist
dist
├── miniseed2mat-0.1.0-py3-none-any.whl
└── miniseed2mat-0.1.0.tar.gz

0 directories, 2 files

Now, your project is ready to be distributed. You can install it by the command

poetry install

And this will install all the dependencies.

Check your understanding

What two artifacts does poetry build produce in the dist/ folder?

Publish your package to PyPI

Now, we are ready to publish the package to PyPI. However, you need an account on PyPI and create an API token.

$ poetry config pypi-token.pypi <TOKEN> 
$ poetry publish

Publishing miniseed2mat (0.1.0) to PyPI
 - Uploading miniseed2mat-0.1.0-py3-none-any.whl 100%
 - Uploading miniseed2mat-0.1.0.tar.gz 100%

Now, you package should be available to install using pip.

Even better than a token: Trusted Publishing. For CI/CD (e.g. GitHub Actions), PyPI now supports Trusted Publishing via OpenID Connect — you register your repo as a “trusted publisher” and publish with a short-lived OIDC token, so there’s no long-lived API token to store or leak. It’s the recommended way to publish from automation today.

Install and test your package

Now, I will install my package using pip and test in some external directory.

cd ~/Downloads
mkdir testminiseed2mat
pip install miniseed2mat

Now, I create a script:

from miniseed2mat.main import convertmseed2mat
mseedfile = "myStream.mseed"
convertmseed2mat(mseedfile, output_mat=None)

Next, we execute the script:

$ python test_env.py 
Output file: myStream.mat

Add more details about your project

You can add more details about your project in the pyproject.toml file:

[tool.poetry]
name = "miniseed2mat"
version = "0.1.7"
description = "Analyzing MiniSEED seismic data in MATLAB"
homepage="https://www.earthinversion.com/utilities/converting-mseed-data-to-mat-and-analyzing-in-matlab/"
authors = ["Utpal Kumar <utpalkumar50@gmail.com>"]
documentation= "https://github.com/earthinversion/convert-mseed2mat"
keywords=["matlab", "seismic waveforms", "python to matlab", "seismology", "miniseed"]
readme="README.md"

[tool.poetry.dependencies]
python = ">=3.9,<3.11"
scipy = "^1.7.3"
obspy = "^1.2.2"

[tool.poetry.dev-dependencies]
pytest = "^5.2"

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.scripts]
mseed2mat = "miniseed2mat.main:convertmseed2mat"

Updating package

If you want to update any changes in your package, you can first make the changes. Then update the version number in the file pyproject.toml and other locations.

poetry build
poetry publish

Recap

Without scrolling up — what’s the Poetry publishing flow?

  • poetry new scaffolds a project with a single pyproject.toml (deps, metadata, entry points).
  • poetry add records dependencies; poetry build produces a wheel + sdist in dist/.
  • poetry publish uploads to PyPI using an API token (or Trusted Publishing in CI).
  • Bump the version, build, publish again to release an update.

One config file and four commands replace the whole setup.py/twine/virtualenv dance.

Where to go next

Disclaimer of liability

The information provided by the Earth Inversion is made available for educational purposes only.

Whilst we endeavor to keep the information up-to-date and correct. Earth Inversion makes no representations or warranties of any kind, express or implied about the completeness, accuracy, reliability, suitability or availability with respect to the website or the information, products, services or related graphics content on the website for any purpose.

UNDER NO CIRCUMSTANCE SHALL WE HAVE ANY LIABILITY TO YOU FOR ANY LOSS OR DAMAGE OF ANY KIND INCURRED AS A RESULT OF THE USE OF THE SITE OR RELIANCE ON ANY INFORMATION PROVIDED ON THE SITE. ANY RELIANCE YOU PLACED ON SUCH MATERIAL IS THEREFORE STRICTLY AT YOUR OWN RISK.


Leave a comment