Guidance for creating Python packages and serving them via a local PyPI server. This skill applies when tasks involve building Python packages (with pyproject.toml or setup.py), setting up local package repositories, or serving packages via HTTP for pip installation. Use when the goal is to create installable Python packages and make them available through a local index URL.
This skill provides guidance for creating Python packages and serving them through a local PyPI server for installation via pip.
This skill applies to tasks that involve:
pip install --index-urlBefore starting any work, assess the execution environment to avoid compatibility issues:
# Check Python version - critical for tool compatibility
python3 --version
# Check available package tools
which pip3 && pip3 --version
which uv 2>/dev/null # Fast package manager alternative
# Check for process management tools
which pkill lsof fuser 2>/dev/null || echo "Limited process tools"
Key compatibility consideration: Python 3.13+ removed the cgi module, which breaks pypiserver. Plan for fallback approaches if using Python 3.13+.
Use Python's built-in http.server module - no external dependencies, works across all Python versions.
Directory structure required:
server-root/
└── simple/
└── packagename/
├── packagename-0.1.0.tar.gz
└── packagename-0.1.0-py3-none-any.whl
Server command:
cd server-root && python3 -m http.server 8080
More feature-rich but has Python version constraints.
pip install pypiserver
pypi-server run -p 8080 ./packages/
Note: The command is pypi-server (with hyphen), not pypiserver.
packagename/
├── pyproject.toml
├── src/
│ └── packagename/
│ └── __init__.py
Or flat layout:
packagename/
├── pyproject.toml
└── packagename/
└── __init__.py
Minimal configuration:
[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "packagename"
version = "0.1.0"
description = "Package description"
requires-python = ">=3.8"
cd packagename
pip install build
python -m build
This creates dist/packagename-0.1.0.tar.gz and dist/packagename-0.1.0-py3-none-any.whl.
mkdir -p pypi-server/simple/packagename
cp packagename/dist/* pypi-server/simple/packagename/
Important: Track the working directory from which the server starts. The server will only serve files relative to its starting directory.
cd pypi-server && python3 -m http.server 8080 &
Record the PID for later cleanup:
echo $! > server.pid
# Check root endpoint
curl -s http://localhost:8080/
# Check simple index (this is what pip uses)
curl -s http://localhost:8080/simple/
# Check package listing
curl -s http://localhost:8080/simple/packagename/
pip install --index-url http://localhost:8080/simple packagename==0.1.0
import packagename
# Test the actual functionality
/)/simple/ endpoint/simple/packagename/pip install succeeds with explicit index URL# Start with explicit working directory tracking
cd /path/to/server-root
python3 -m http.server 8080 &
SERVER_PID=$!
echo "Server PID: $SERVER_PID"
# Check if port is in use
python3 -c "import socket; s=socket.socket(); s.settimeout(1); result=s.connect_ex(('localhost', 8080)); print('Port in use' if result==0 else 'Port free'); s.close()"
# List processes on port (if tools available)
lsof -i :8080 2>/dev/null || fuser 8080/tcp 2>/dev/null
# If PID is known
kill $SERVER_PID
# If PID unknown, find and kill
pkill -f "http.server 8080" 2>/dev/null
# Fallback: use /proc filesystem
for pid in /proc/[0-9]*; do
if grep -q "http.server" "$pid/cmdline" 2>/dev/null; then
kill "$(basename $pid)" 2>/dev/null
fi
done
Symptom: Server running but /simple/ returns 404.
Cause: A previous server instance started from a different directory is still running.
Solution: Kill all HTTP server processes, verify port is free, then restart from the correct directory.
Symptom: "Address already in use" error when starting server.
Solution: Find and kill the process using the port, or use a different port.
Symptom: Import error about missing cgi module.
Solution: Use the built-in http.server approach instead.
Symptom: pip install fails with "No matching distribution found."
Possible causes:
/simple/packagename/)Debug approach:
# Verify server response includes package files
curl http://localhost:8080/simple/packagename/
Symptom: pip install succeeds but import packagename fails.
Cause: Package structure issue - the importable name may differ from the package name.
Solution: Verify __init__.py exists and the package structure matches pyproject.toml configuration.
Before starting server operations, ensure a clean state:
This prevents confusion from orphaned processes or stale state.