Install & Compatibility
Where this runs
tested against v7.0.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.000s · 62.4MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 5.6s · import 0.000s · 62MB
61MB installed
● package 61MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
twine (CLI)
✓ twine upload dist/*
Twine is primarily a command-line utility for package publishing. Direct programmatic import of its core upload functionality is not the common or recommended usage pattern for end-users.
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/*
twine --version
Debug
Known issues
breakingThe `python setup.py upload` command is deprecated and insecure. Twine is the official, secure, and recommended tool for uploading packages to PyPI.fixAlways use `twine upload` instead of `python setup.py upload`. Ensure you build your distribution artifacts first using `python -m build`.
affects: < 4.0.0 (and continued deprecation warnings in newer Python versions)
breakingSince 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.fixWhen 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`.
affects: >= 5.0.0
breakingTwine 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.fixExplicitly 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.
affects: >= 6.0.0
deprecatedThe `--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.fixRemove `--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.
affects: >= 6.2.0
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`.fixEnsure 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.
affects: All versions (with `pyproject.toml` dynamic readme)
gotchaUsing `twine upload dist/*` can inadvertently upload older or incorrect distribution files if your `dist/` directory contains multiple versions or unwanted artifacts.fixAlways 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-*`.
affects: All versions
breakingAttempting 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.fixTo 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.
affects: All Python versions
breakingPlacing 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.fixShell 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.
affects: All Python versions
Upgrade
Version history
7.0.0latest on PyPI · released Jul 27, 2026
Audit
Dependencies
keyringoptionalUsed for secure storage of PyPI credentials; highly recommended for production environments.
readme-rendererrequiredUsed by `twine check` to validate how your package's long description will render on PyPI.
requestsrequiredCore HTTP library for making authenticated uploads.