This guide explains how to build and publish the puzzel-sms-gateway-client package to PyPI using uv.
- Prerequisites
- Bump the Version
- Build the Package
- Publish to TestPyPI (Recommended First)
- Publish to PyPI
- Verify the Release
- Credentials Reference
- uv installed — see 01-uv-setup.md.
- PyPI account — register at pypi.org.
- API token — generate a token at Account Settings → API tokens on PyPI (and optionally on test.pypi.org for the test step).
Use API tokens rather than username/password. Tokens are scoped to a single project and can be revoked without changing your account password.
Update the version in pyproject.toml before every release:
[project]
version = "3.1.0" # ← change thisFollow Semantic Versioning:
| Change type | Example |
|---|---|
| Bug fix / patch | 3.0.0 → 3.0.1 |
| New feature (backwards-compatible) | 3.0.0 → 3.1.0 |
| Breaking change | 3.0.0 → 4.0.0 |
From the clients/smsgw-client-python/ directory:
uv buildThis produces two artefacts in dist/:
dist/
├── puzzel_sms_gateway_client-3.1.0-py3-none-any.whl # wheel (binary distribution)
└── puzzel_sms_gateway_client-3.1.0.tar.gz # sdist (source distribution)
Inspect the wheel contents to confirm everything looks correct before uploading:
uv run python -m zipfile -l dist/puzzel_sms_gateway_client-*.whlUpload to test.pypi.org first to verify the package installs and renders correctly without affecting the real index.
uv publish \
--publish-url https://test.pypi.org/legacy/ \
--token pypi-your-test-token-hereThen install from TestPyPI and do a quick smoke test:
uv pip install \
--index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
puzzel-sms-gateway-client==3.1.0The
--extra-index-urlflag allows pip to resolve dependencies (Kiota packages) from the real PyPI if they are not mirrored on TestPyPI.
Once you are satisfied with the TestPyPI release, publish to the real index:
uv publish --token pypi-your-production-token-hereuv publish defaults to https://upload.pypi.org/legacy/, so no --publish-url is needed for the main PyPI.
Instead of passing the token on the command line every time, set it as an environment variable:
export UV_PUBLISH_TOKEN=pypi-your-production-token-here
uv publishOr store it in a .env file (make sure .env is listed in .gitignore):
# .env
UV_PUBLISH_TOKEN=pypi-your-production-token-here# Load it before publishing
source .env && uv publishYou can also configure credentials once in ~/.pypirc so you never have to pass them explicitly:
[distutils]
index-servers =
pypi
testpypi
[pypi]
repository = https://upload.pypi.org/legacy/
username = __token__
password = pypi-your-production-token-here
[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-your-test-token-hereWith this in place, publishing is just:
uv publish # → PyPI
uv publish --publish-url https://test.pypi.org/legacy/ # → TestPyPI
~/.pypircis read by bothuv publishandtwine. Keep this file private (chmod 600 ~/.pypirc).
After a successful upload, confirm the package is live:
# Install the freshly published version
uv pip install "puzzel-sms-gateway-client==3.1.0"
# Quick import check
python -c "from src.mt_http_client import MtHttpClient; print('OK')"Check the PyPI project page to ensure the description, classifiers, and links rendered correctly:
https://pypi.org/project/puzzel-sms-gateway-client/
| Variable / file | Purpose |
|---|---|
UV_PUBLISH_TOKEN |
API token passed via environment variable |
~/.pypirc |
Persistent credential store (both [pypi] and [testpypi] sections) |
--token flag |
One-off token passed directly to uv publish |
Never commit tokens or ~/.pypirc contents to version control.