Packaging means creating a package that can be installed by pip
.
There are many ways to create an installable package, and many ways to distribute it.
We will show how to create a package using hatchling
, and how to distribute it on GitHub, PyPI and a private PyPI server.
pip
pyproject.toml
in the root folder of the projectmyproject-0.1.0-py3-none-any.whl
)pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "my_library"
version = "0.0.1"
dependencies = [
"numpy"
]
authors = [
{ name="First Last", email="initials@dhigroup.com" },
]
description = "Useful library"
readme = "README.md"
requires-python = ">=3.9"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Development Status :: 2 - Pre-Alpha",
"Operating System :: OS Independent",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering",
]
[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy", "mkdocs", "mkdocstrings[python]", "mkdocs-material"]
test= ["pytest"]
[project.urls]
"Homepage" = "https://github.com/DHI/my_library"
"Bug Tracker" = "https://github.com/DHI/my_library/issues"
Versioning your package is important for reproducibility and to avoid breaking changes.
Semantic versioning use three numbers {major}.{minor}.{patch}
, e.g. 1.1.0
1.0.0
is more recent than 0.24.12
1.0
indicates that the package is ready for production0.1.0
and increase the version number as you add featuresWhat is a breaking change?
Try to avoid breaking changes, if possible, but if you do, increase the major version number!
pip install my_library
will install the latest versionpip install my_library==1.0.0
will install version 1.0.0pip install my_library>=1.0.0
will install version 1.0.0 or higher1.0.0rc1
pip install my_library==1.0.0rc1
pyproject.toml
fileApplication
A program that is run by a user
Pin versions to ensure reproducibility, e.g. numpy==1.11.0
Library
A program that is used by another program
Make the requirements as loose as possible, e.g. numpy>=1.11.0
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "my_library"
version = "0.0.1"
dependencies = [
"numpy"
]
authors = [
{ name="First Last", email="initials@dhigroup.com" },
]
description = "Useful library"
readme = "README.md"
requires-python = ">=3.9"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Development Status :: 2 - Pre-Alpha",
"Operating System :: OS Independent",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering",
]
[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy", "mkdocs", "mkdocstrings[python]", "mkdocs-material"]
test= ["pytest"]
[project.urls]
"Homepage" = "https://github.com/DHI/my_library"
"Bug Tracker" = "https://github.com/DHI/my_library/issues"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Development Status :: 2 - Pre-Alpha",
"Operating System :: OS Independent",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering",
]
hatchling
, you can include non-Python files in your package.hatchling
uses .gitignore to determine which files to include.To use secrets as environment variables in GitHub Actions, add them to the env
section of the workflow:
.github/workflows/python_publish.yml
name: Publish Python Package
on:
release:
types: [created]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install build
- name: Build package
run: python -m build
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
pip
pip install https://github.com/DHI/mikeio/archive/main.zip
fix-interp
branch, e.g. pip install https://github.com/DHI/mikeio/archive/fix-interp.zip
import datetime
from dataclasses import dataclass
@dataclass
class Interval:
start: date
end: date
>>> dr1 = Interval(start=datetime.date(2020, 1, 1), end=datetime.date(2020, 1, 31))
>>> dr1
Interval(start=datetime.date(2020, 1, 1), end=datetime.date(2020, 1, 31))
>>> dr2 = Interval(start=datetime.date(2020, 1, 1), end=datetime.date(2020, 1, 31))
>>> dr1 == dr2
True
If you want your code to be Pythonic, you have to be familiar with these types and their methods.
Dundermethods:
__getitem__
__setitem__
__len__
__contains__
An example is a Scikit learn transformers
fit
transform
fit_transform
If you want to make a transformer compatible with sklearn, you have to implement these methods.
Definition โUnitโ
Python package development