PyPI Publisher
Complete workflow for publishing Python packages to PyPI with best practices, error handling, and automation.
Critical Pre-Publish Checklist
ALWAYS run tests before publishing. Never skip this step.
-
Run full test suite
python -m pytest tests/ -v --tb=short- All tests must pass
- Check code coverage if applicable
- Fix any failures before proceeding
-
Verify version numbers are consistent
pyproject.toml:version = "X.Y.Z"package/__init__.py:__version__ = "X.Y.Z"- Any other version references (e.g.,
mcp_server.pyserver_version)
-
Check for uncommitted changes
- Review git status
- Commit or stash changes as needed
-
Clean old build artifacts
# Windows PowerShell Remove-Item -Recurse -Force dist, build, *.egg-info -ErrorAction SilentlyContinue # Linux/Mac rm -rf dist/ build/ *.egg-info
Publishing Workflow
Step 1: Build Package
uv build
# or
python -m build
Verify output:
dist/package-name-X.Y.Z.tar.gz(source distribution)dist/package_name-X.Y.Z-py3-none-any.whl(wheel)
Step 2: Validate Package
uvx twine check dist/*
Must pass before uploading. Fix any warnings or errors.
Step 3: Choose Publishing Target
Test PyPI (recommended for first-time publishing):
uvx twine upload --repository testpypi dist/*
Production PyPI:
uvx twine upload dist/*
Step 4: Authentication
Using API Token (recommended):
Windows PowerShell:
$env:TWINE_USERNAME = "__token__"
$env:TWINE_PASSWORD = "pypi-your-api-token-here"
uvx twine upload dist/* --non-interactive
Linux/Mac:
export TWINE_USERNAME=__token__
export TWINE_PASSWORD=pypi-your-api-token-here
uvx twine upload dist/* --non-interactive
Create API Token:
- Go to https://pypi.org/manage/account/token/
- Create new token (scope: entire account or specific project)
- Copy token immediately (only shown once)
- Use as password with username
__token__
Common Errors and Solutions
"File already exists" (400 Bad Request)
Cause: Version already published to PyPI.
Solution:
- Increment version number in
pyproject.tomland__init__.py - Rebuild package
- Upload again
Version numbering:
- Patch:
0.1.0→0.1.1(bug fixes) - Minor:
0.1.0→0.2.0(new features, backward compatible) - Major:
0.1.0→1.0.0(breaking changes)
"NonInteractive: Credential not found"
Cause: Missing authentication credentials.
Solution:
- Set
TWINE_USERNAMEandTWINE_PASSWORDenvironment variables - Or use
--non-interactiveflag with credentials - Or create
~/.pypircconfig file
"Package check failed"
Cause: Package metadata or structure issues.
Solution:
- Run
twine check dist/*to see specific errors - Fix metadata in
pyproject.toml - Rebuild and check again
Import errors after publishing
Cause: Package structure or import paths incorrect.
Solution:
- Verify
[tool.hatch.build.targets.wheel]packages configuration - Check
__init__.pyfiles exist - Test local installation:
pip install -e .
Automated Publishing Script
For PowerShell (Windows):
# Run tests first
python -m pytest tests/ -v --tb=short
if ($LASTEXITCODE -ne 0) {
Write-Host "Tests failed! Aborting publish." -ForegroundColor Red
exit 1
}
# Build
uv build
if ($LASTEXITCODE -ne 0) { exit 1 }
# Check
uvx twine check dist/*
if ($LASTEXITCODE -ne 0) { exit 1 }
# Upload (set credentials first)
$env:TWINE_USERNAME = "__token__"
$env:TWINE_PASSWORD = "your-token"
uvx twine upload dist/* --non-interactive
Version Management
Always update version in these locations:
pyproject.toml:version = "X.Y.Z"package/__init__.py:__version__ = "X.Y.Z"- Any hardcoded version strings in code (e.g., MCP server version)
Semantic versioning:
- MAJOR: Breaking changes
- MINOR: New features (backward compatible)
- PATCH: Bug fixes
Post-Publish Verification
-
Check PyPI page:
https://pypi.org/project/your-package-name/X.Y.Z/ -
Test installation:
pip install your-package-name==X.Y.Z -
Verify functionality:
- Import package
- Run basic functionality
- Check CLI commands work (if applicable)
Best Practices
-
Always test before publishing
- Run full test suite
- Test actual package installation
- Verify end-to-end functionality
-
Use Test PyPI first
- Test new publishing workflow
- Verify package metadata
- Catch issues before production
-
Use API tokens, not passwords
- More secure
- Can be scoped to specific projects
- Can be revoked easily
-
Version consistently
- Follow semantic versioning
- Update all version references
- Document changes in CHANGELOG
-
Automate what you can
- Use scripts for repetitive steps
- Include test runs in automation
- Add validation checks
-
Handle errors gracefully
- Check exit codes
- Provide clear error messages
- Stop on failures (don't continue with broken state)
Quick Reference
Essential commands:
# Test
python -m pytest tests/ -v
# Build
uv build
# Check
uvx twine check dist/*
# Upload (Test PyPI)
uvx twine upload --repository testpypi dist/*
# Upload (Production)
uvx twine upload dist/*
Version update locations:
pyproject.tomlpackage/__init__.py- Code files with version strings
Common file patterns:
dist/*.whl- Wheel distributiondist/*.tar.gz- Source distributionbuild/- Temporary build files (can delete)*.egg-info/- Package metadata (can delete)
Automated Publishing Scripts
Pre-built scripts are available in scripts/:
-
publish.ps1- PowerShell script for Windows.\publish.ps1 --test # Publish to Test PyPI .\publish.ps1 --token "pypi-xxx" # Publish to Production with token -
publish.sh- Bash script for Linux/Macchmod +x publish.sh ./publish.sh --test # Publish to Test PyPI ./publish.sh --token "pypi-xxx" # Publish to Production with token
These scripts automatically:
- Run tests before publishing
- Build and validate package
- Handle authentication
- Provide clear error messages