Publishing a Package to PyPI
Recommended project layout
your-project/
├─ src/
│ └─ mypkg/
│ ├─ __init__.py
│ └─ core.py
├─ tests/
│ └─ test_core.py
├─ README.md
├─ LICENSE
├─ .gitignore
└─ pyproject.toml
Create pyproject.toml
[build-system]
requires = ["setuptools>=68"] # PEP 621 support
build-backend = "setuptools.build_meta"
[project]
name = "mypkg" # must be unique on PyPI
version = "0.1.0" # bump for every release
description = "My package does awesome things."
readme = "README.md"
requires-python = ">=3.8"
license = { file = "LICENSE" }
authors = [{ name = "Your Name", email = "you@example.com" }]
keywords = ["example", "demo", "tutorial"]
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
# "requests>=2.31",
]
[project.optional-dependencies]
dev = ["pytest>=8"]
[project.urls]
Homepage = "https://github.com/yourname/mypkg"
Issues = "https://github.com/yourname/mypkg/issues"
Documentation = "https://github.com/yourname/mypkg#readme"
# Console script (optional): installs a `mypkg` command
[project.scripts]
mypkg = "mypkg.cli:main"
# setuptools package discovery for src layout (optional)
[tool.setuptools.packages.find]
where = ["src"]
# Include package data (optional)
[tool.setuptools.package-data]
"mypkg" = ["py.typed", "data/*.txt"]
Build and publish
pip install --upgrade build twine
# Build the package
python -m build
# Upload to PyPI (test or live)
twine upload dist/*
🔴 Crucial note: keep dist/ only for the current version
PyPI will not let you re-upload the same filename (filenames encode name + version + build tags). If dist/ contains old artifacts or duplicates, you’ll get “File already exists” or version/file conflicts.
Always clear dist/ before building so it holds only the new version:
rm -rf dist && python -m build
You cannot overwrite an uploaded file on PyPI. If you published the wrong build, bump the version and upload again.
.pypirc (optional)
To avoid entering credentials every time, you can create a .pypirc
file in your home directory:
[pypi]
username = __token__
password = your-token
Enjoy Reading This Article?
Here are some more articles you might like to read next: