twine

raw JSON →
6.2.0 verified Tue May 12 auth: no python install: stale

Twine is a utility for publishing Python packages to PyPI and other repositories. It provides build system independent uploads of source and binary distribution artifacts, enhancing security and testability compared to older methods. The current version is 6.2.0, released on September 4, 2025, and it is actively maintained with frequent updates.

pip install twine
error twine: command not found
cause Twine is not installed or its executable location is not included in your system's PATH.
fix
Run pip install twine to install it, or ensure the Python scripts directory (where twine is installed) is added to your system's PATH.
error ERROR (400) The filename <filename> is already in use. Please use a different version.
cause You are attempting to upload a package with a version number that has already been published to PyPI (or the target repository).
fix
Increment the version number of your package in your pyproject.toml (or setup.py) and rebuild the distribution files (python -m build) before attempting to upload again.
error ERROR Authentication Failed: 403 Forbidden
cause The username/API token and password combination provided to Twine are incorrect or lack the necessary permissions for the target repository.
fix
Verify your PyPI username and API token (or password) by checking your PyPI account settings or regenerating a new API token. Ensure you're using __token__ as the username for API tokens.
error No files to upload found in <directory_path>/dist/
cause Twine could not find any distribution files (.whl or .tar.gz) in the specified or default `dist/` directory.
fix
First, generate the distribution files by running python -m build (or python setup.py sdist bdist_wheel) in your project's root directory, which will create them in the dist/ folder.
breaking The `python setup.py upload` command is deprecated and insecure. Twine is the official, secure, and recommended tool for uploading packages to PyPI.
fix Always use `twine upload` instead of `python setup.py upload`. Ensure you build your distribution artifacts first using `python -m build`.
breaking Since Twine 5.0.0, only `__token__` is supported as a username when uploading to PyPI and TestPyPI. Supplying any other username will cause the upload to fail.
fix When using API tokens, always set the username to `__token__`. Your API token should be used as the password. This applies whether using environment variables (`TWINE_USERNAME`, `TWINE_PASSWORD`), `.pypirc`, or `keyring`.
breaking Twine 6.0.0 changed the default username behavior for PyPI/TestPyPI. It now defaults to `__token__` and no longer overrides a username configured via environment variables or command line if that username is *not* `__token__`. Workflows that implicitly relied on Twine overriding a non-`__token__` username will now fail.
fix Explicitly set `TWINE_USERNAME='__token__'` (or use `--username __token__` on the CLI) if you intend to use an API token. Do not rely on previous implicit override behavior.
deprecated The `--skip-existing` flag is no longer supported for non-PyPI upload targets (e.g., custom package indexes). It also no longer submits `md5_digest` field as it's deprecated on PyPI.
fix Remove `--skip-existing` when uploading to non-PyPI repositories. For PyPI, the flag will still work but consider the implications. The `md5_digest` removal is automatic and requires no user action.
gotcha `twine check dist/*` may fail if your `README` file is not named exactly `README` and you are using the dynamic `readme` option in `pyproject.toml`.
fix Ensure your `README` file is named `README` (e.g., `README.md`) if using `dynamic = ["readme"]` in `pyproject.toml` for metadata. Despite the check failure, the package might still upload successfully, but it's best to resolve the warning for proper rendering.
gotcha Using `twine upload dist/*` can inadvertently upload older or incorrect distribution files if your `dist/` directory contains multiple versions or unwanted artifacts.
fix Always ensure your `dist/` directory contains only the desired distribution files for the current upload. Consider clearing the `dist/` directory before building new distributions or being more specific with the file path, e.g., `twine upload dist/my_package-1.2.3-*`.
breaking Attempting to execute shell commands like `python -m build` directly as statements within a Python script will result in a `SyntaxError`. These are shell commands, not valid Python syntax.
fix To execute shell commands from within a Python script, use the `subprocess` module (e.g., `import subprocess; subprocess.run(['python', '-m', 'build'], check=True)`). Alternatively, ensure the script containing such commands is intended to be a shell script and is executed directly by the shell, not by the Python interpreter.
breaking Placing shell commands, such as `python -m build`, directly into a Python script will cause a `SyntaxError` when the script is executed by the Python interpreter.
fix Shell commands must be run in a shell environment (e.g., a `.sh` script, directly in the terminal, or by using `subprocess.run()` if called from Python code). Do not include them directly as lines of code in a `.py` file.
python os / libc status wheel install import disk
3.10 alpine (musl) wheel - - 61.4M
3.10 alpine (musl) - - - -
3.10 slim (glibc) wheel 5.3s - 61M
3.10 slim (glibc) - - - -
3.11 alpine (musl) wheel - - 66.2M
3.11 alpine (musl) - - - -
3.11 slim (glibc) wheel 5.3s - 66M
3.11 slim (glibc) - - - -
3.12 alpine (musl) wheel - - 56.7M
3.12 alpine (musl) - - - -
3.12 slim (glibc) wheel 4.6s - 57M
3.12 slim (glibc) - - - -
3.13 alpine (musl) wheel - - 56.5M
3.13 alpine (musl) - - - -
3.13 slim (glibc) wheel 4.5s - 57M
3.13 slim (glibc) - - - -
3.9 alpine (musl) wheel - - 61.1M
3.9 alpine (musl) - - - -
3.9 slim (glibc) wheel 6.4s - 61M
3.9 slim (glibc) - - - -

This quickstart guides you through building your Python package distributions (sdist and wheel), checking them for common issues, and then uploading them to TestPyPI for verification before a final upload to the official PyPI. It emphasizes using API tokens for authentication via environment variables for security.

# 1. Ensure your distribution files (wheels, sdists) are built:
# (Use `python -m build` as a modern alternative to `python setup.py sdist bdist_wheel`)
python -m build

# 2. (Optional but recommended) Check your package metadata and README rendering:
twine check dist/*

# 3. (Optional but recommended) Upload to TestPyPI first to verify:
# Set TWINE_USERNAME and TWINE_PASSWORD environment variables with your TestPyPI API token
# For example, in bash:
# export TWINE_USERNAME="__token__"
# export TWINE_PASSWORD="pypi-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# Or, you will be prompted for credentials.
# Replace 'your_test_pypi_api_token' with your actual TestPyPI API token
import os
os.environ['TWINE_USERNAME'] = os.environ.get('TEST_PYPI_USERNAME', '__token__')
os.environ['TWINE_PASSWORD'] = os.environ.get('TEST_PYPI_PASSWORD', 'pypi-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')

print("Uploading to TestPyPI...")
# This command will be run via subprocess in a real application
# For quickstart, it implies running directly in shell:
# !twine upload --repository testpypi dist/*
# In a Python context, you'd use subprocess.run()

# 4. Upload to PyPI (after successful TestPyPI verification):
# Set TWINE_USERNAME and TWINE_PASSWORD environment variables with your PyPI API token
# For example, in bash:
# export TWINE_USERNAME="__token__"
# export TWINE_PASSWORD="pypi-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
# Or, you will be prompted for credentials.
# Replace 'your_pypi_api_token' with your actual PyPI API token
os.environ['TWINE_USERNAME'] = os.environ.get('PYPI_USERNAME', '__token__')
os.environ['TWINE_PASSWORD'] = os.environ.get('PYPI_PASSWORD', 'pypi-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY')

print("Uploading to PyPI...")
# !twine upload dist/*