Skip to content

PyPI Repository Guide

Artifact Keeper provides a PEP 503-compliant Simple Repository API for hosting Python packages.

Endpoint

PyPI operations use the /pypi endpoint:

http://localhost:8080/pypi

This endpoint implements the PEP 503 Simple Repository API.

Configuration

pip.conf

Configure pip to use Artifact Keeper as the package index.

Linux/macOS

Create or edit ~/.config/pip/pip.conf:

[global]
index-url = http://localhost:8080/pypi/simple
trusted-host = localhost

Windows

Create or edit %APPDATA%\pip\pip.ini:

[global]
index-url = http://localhost:8080/pypi/simple
trusted-host = localhost

Project-Level Configuration

Create pip.conf in your project root:

[global]
index-url = http://localhost:8080/pypi/simple
trusted-host = localhost

Environment Variable

Set index URL via environment variable:

Terminal window
export PIP_INDEX_URL=http://localhost:8080/pypi/simple
export PIP_TRUSTED_HOST=localhost

Installing Packages

Install from Artifact Keeper

Terminal window
pip install my-package

Install Specific Version

Terminal window
pip install my-package==1.0.0
pip install my-package>=1.0.0
pip install my-package~=1.0.0

Install from Command Line

Override index URL for a single command:

Terminal window
pip install --index-url http://localhost:8080/pypi/simple my-package

Install with Requirements File

Terminal window
pip install -r requirements.txt

With custom index in requirements.txt:

--index-url http://localhost:8080/pypi/simple
--trusted-host localhost
my-package==1.0.0
another-package>=2.0.0

Publishing Packages

Prepare Your Package

Ensure you have a proper package structure:

my-package/
├── pyproject.toml
├── setup.py (or setup.cfg)
├── README.md
├── LICENSE
└── my_package/
└── __init__.py

pyproject.toml

[build-system]
requires = ["setuptools>=61.0", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "my-package"
version = "1.0.0"
description = "My awesome package"
authors = [{name = "Your Name", email = "you@example.com"}]
license = {text = "MIT"}
readme = "README.md"
requires-python = ">=3.8"
dependencies = [
"requests>=2.28.0",
]
[project.urls]
Homepage = "https://github.com/yourusername/my-package"
Repository = "https://github.com/yourusername/my-package"

Build Distribution

Terminal window
# Install build tools
pip install build
# Build wheel and source distribution
python -m build

This creates files in dist/:

  • my_package-1.0.0-py3-none-any.whl (wheel)
  • my-package-1.0.0.tar.gz (source distribution)

Upload with twine

Install twine:

Terminal window
pip install twine

Configure .pypirc

Create ~/.pypirc:

[distutils]
index-servers =
artifact-keeper
[artifact-keeper]
repository = http://localhost:8080/pypi
username = your-username
password = your-password

Upload Package

Terminal window
twine upload --repository artifact-keeper dist/*

Or specify repository URL directly:

Terminal window
twine upload \
--repository-url http://localhost:8080/pypi \
--username your-username \
--password your-password \
dist/*

Upload Specific Files

Terminal window
twine upload --repository artifact-keeper dist/my_package-1.0.0-py3-none-any.whl

Multiple Indexes

Fallback to PyPI

Use Artifact Keeper as primary, fallback to PyPI:

[global]
index-url = http://localhost:8080/pypi/simple
extra-index-url = https://pypi.org/simple

Or via command line:

Terminal window
pip install \
--index-url http://localhost:8080/pypi/simple \
--extra-index-url https://pypi.org/simple \
my-package

Multiple Private Indexes

[global]
index-url = http://localhost:8080/pypi/simple
extra-index-url =
http://team-registry:8080/pypi/simple
http://prod-registry:8080/pypi/simple

Authentication

Basic Authentication

Include credentials in URL:

Terminal window
pip install --index-url http://username:password@localhost:8080/pypi/simple my-package

Token Authentication

Use API token instead of password:

[artifact-keeper]
repository = http://localhost:8080/pypi
username = __token__
password = your-api-token

Keyring Support

Store credentials securely using keyring:

Terminal window
pip install keyring
keyring set http://localhost:8080/pypi username

Poetry Support

Artifact Keeper works seamlessly with Poetry.

Configure Poetry

Terminal window
poetry config repositories.artifact-keeper http://localhost:8080/pypi
poetry config http-basic.artifact-keeper username password

Install with Poetry

Terminal window
poetry add my-package --source artifact-keeper

Publish with Poetry

Terminal window
poetry publish --repository artifact-keeper

pyproject.toml Configuration

[[tool.poetry.source]]
name = "artifact-keeper"
url = "http://localhost:8080/pypi/simple"
priority = "primary"
[[tool.poetry.source]]
name = "pypi"
priority = "supplemental"

PDM Support

Configure PDM

Terminal window
pdm config pypi.url http://localhost:8080/pypi/simple

Install with PDM

Terminal window
pdm add my-package

Publish with PDM

Terminal window
pdm publish --repository http://localhost:8080/pypi

Integration with CI/CD

GitHub Actions

name: Publish Python Package
on:
push:
tags:
- 'v*'
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install build twine
- name: Build package
run: python -m build
- name: Publish to Artifact Keeper
env:
TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
TWINE_REPOSITORY_URL: http://registry.example.com/pypi
run: twine upload dist/*

GitLab CI

publish:
image: python:3.11
stage: deploy
script:
- pip install build twine
- python -m build
- twine upload --repository-url $PYPI_REPOSITORY_URL -u $PYPI_USERNAME -p $PYPI_PASSWORD dist/*
only:
- tags

Jenkins Pipeline

pipeline {
agent any
stages {
stage('Build and Publish') {
steps {
sh '''
python -m venv venv
. venv/bin/activate
pip install build twine
python -m build
twine upload \
--repository-url http://registry.example.com/pypi \
-u $PYPI_USERNAME \
-p $PYPI_PASSWORD \
dist/*
'''
}
}
}
}

Package Versioning

Semantic Versioning

Follow PEP 440 version scheme:

Major.Minor.Patch
"1.0.0"
# Pre-release versions
"1.0.0a1" # Alpha
"1.0.0b1" # Beta
"1.0.0rc1" # Release Candidate
# Post-release
"1.0.0.post1"
# Development release
"1.0.0.dev1"

Version Constraints

In requirements.txt or pyproject.toml:

# Exact version
my-package==1.0.0
# Compatible release (>= 1.0.0, < 2.0.0)
my-package~=1.0.0
# Minimum version
my-package>=1.0.0
# Version range
my-package>=1.0.0,<2.0.0
# Exclude specific versions
my-package!=1.0.1

Advanced Features

Source Distributions vs Wheels

Upload both for maximum compatibility:

Terminal window
# Build both
python -m build
# Upload both
twine upload dist/*

Platform-Specific Wheels

Build platform-specific wheels:

Terminal window
# Build for specific platform
python -m build --wheel --config-setting="--plat-name=linux_x86_64"

Hash Verification

pip automatically verifies package hashes from the repository.

Generate hashes for requirements:

Terminal window
pip hash my-package-1.0.0-py3-none-any.whl

Use in requirements.txt:

my-package==1.0.0 \
--hash=sha256:abc123...

Troubleshooting

SSL Certificate Errors

For HTTPS registries with self-signed certificates:

[global]
trusted-host = localhost
cert = /path/to/certificate.pem

Upload Failures

Check package metadata:

Terminal window
twine check dist/*

Enable verbose output:

Terminal window
twine upload --verbose dist/*

Installation Issues

Clear pip cache:

Terminal window
pip cache purge

Debug installation:

Terminal window
pip install --verbose my-package

Version Conflicts

List installed packages:

Terminal window
pip list
pip show my-package

Force reinstall:

Terminal window
pip install --force-reinstall my-package

Best Practices

Package Metadata

Include comprehensive metadata in pyproject.toml:

[project]
name = "my-package"
version = "1.0.0"
description = "Short description"
readme = "README.md"
license = {text = "MIT"}
authors = [{name = "Your Name", email = "you@example.com"}]
keywords = ["keyword1", "keyword2"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
]

MANIFEST.in

Control which files are included in source distributions:

include README.md
include LICENSE
recursive-include my_package/data *
exclude .gitignore
exclude .env

.gitignore

dist/
build/
*.egg-info/
__pycache__/
.pytest_cache/
.venv/

See Also