{"id":1034,"library":"twine","title":"twine","description":"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.","status":"active","version":"6.2.0","language":"python","source_language":"en","source_url":"https://github.com/pypa/twine/","tags":["packaging","pypi","publishing","distribution","cli"],"install":[{"cmd":"pip install twine","lang":"bash","label":"Install twine"}],"dependencies":[{"reason":"Used for secure storage of PyPI credentials; highly recommended for production environments.","package":"keyring","optional":true},{"reason":"Used by `twine check` to validate how your package's long description will render on PyPI.","package":"readme-renderer","optional":false},{"reason":"Core HTTP library for making authenticated uploads.","package":"requests","optional":false}],"imports":[{"note":"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.","symbol":"twine (CLI)","correct":"twine upload dist/*"}],"quickstart":{"code":"# 1. Ensure your distribution files (wheels, sdists) are built:\n# (Use `python -m build` as a modern alternative to `python setup.py sdist bdist_wheel`)\npython -m build\n\n# 2. (Optional but recommended) Check your package metadata and README rendering:\ntwine check dist/*\n\n# 3. (Optional but recommended) Upload to TestPyPI first to verify:\n# Set TWINE_USERNAME and TWINE_PASSWORD environment variables with your TestPyPI API token\n# For example, in bash:\n# export TWINE_USERNAME=\"__token__\"\n# export TWINE_PASSWORD=\"pypi-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n# Or, you will be prompted for credentials.\n# Replace 'your_test_pypi_api_token' with your actual TestPyPI API token\nimport os\nos.environ['TWINE_USERNAME'] = os.environ.get('TEST_PYPI_USERNAME', '__token__')\nos.environ['TWINE_PASSWORD'] = os.environ.get('TEST_PYPI_PASSWORD', 'pypi-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')\n\nprint(\"Uploading to TestPyPI...\")\n# This command will be run via subprocess in a real application\n# For quickstart, it implies running directly in shell:\n# !twine upload --repository testpypi dist/*\n# In a Python context, you'd use subprocess.run()\n\n# 4. Upload to PyPI (after successful TestPyPI verification):\n# Set TWINE_USERNAME and TWINE_PASSWORD environment variables with your PyPI API token\n# For example, in bash:\n# export TWINE_USERNAME=\"__token__\"\n# export TWINE_PASSWORD=\"pypi-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY\"\n# Or, you will be prompted for credentials.\n# Replace 'your_pypi_api_token' with your actual PyPI API token\nos.environ['TWINE_USERNAME'] = os.environ.get('PYPI_USERNAME', '__token__')\nos.environ['TWINE_PASSWORD'] = os.environ.get('PYPI_PASSWORD', 'pypi-YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY')\n\nprint(\"Uploading to PyPI...\")\n# !twine upload dist/*","lang":"python","description":"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."},"warnings":[{"fix":"Always use `twine upload` instead of `python setup.py upload`. Ensure you build your distribution artifacts first using `python -m build`.","message":"The `python setup.py upload` command is deprecated and insecure. Twine is the official, secure, and recommended tool for uploading packages to PyPI.","severity":"breaking","affected_versions":"< 4.0.0 (and continued deprecation warnings in newer Python versions)"},{"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`.","message":"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.","severity":"breaking","affected_versions":">= 5.0.0"},{"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.","message":"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.","severity":"breaking","affected_versions":">= 6.0.0"},{"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.","message":"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.","severity":"deprecated","affected_versions":">= 6.2.0"},{"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.","message":"`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`.","severity":"gotcha","affected_versions":"All versions (with `pyproject.toml` dynamic readme)"},{"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-*`.","message":"Using `twine upload dist/*` can inadvertently upload older or incorrect distribution files if your `dist/` directory contains multiple versions or unwanted artifacts.","severity":"gotcha","affected_versions":"All versions"},{"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.","message":"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.","severity":"breaking","affected_versions":"All Python versions"},{"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.","message":"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.","severity":"breaking","affected_versions":"All Python versions"}],"env_vars":null,"last_verified":"2026-05-12T22:53:55.915Z","next_check":"2026-06-27T00:00:00.000Z","problems":[{"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.","cause":"Twine is not installed or its executable location is not included in your system's PATH.","error":"twine: command not found"},{"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.","cause":"You are attempting to upload a package with a version number that has already been published to PyPI (or the target repository).","error":"ERROR (400) The filename <filename> is already in use. Please use a different version."},{"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.","cause":"The username/API token and password combination provided to Twine are incorrect or lack the necessary permissions for the target repository.","error":"ERROR Authentication Failed: 403 Forbidden"},{"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.","cause":"Twine could not find any distribution files (.whl or .tar.gz) in the specified or default `dist/` directory.","error":"No files to upload found in <directory_path>/dist/"}],"ecosystem":"pypi","meta_description":null,"install_score":0,"install_tag":"stale","quickstart_score":null,"quickstart_tag":null,"pypi_latest":"6.2.0","cli_name":"twine","install_checks":{"last_tested":"2026-05-12","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"61.4M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":5.3,"import_time_s":null,"mem_mb":null,"disk_size":"61M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"66.2M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":5.3,"import_time_s":null,"mem_mb":null,"disk_size":"66M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"56.7M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":4.6,"import_time_s":null,"mem_mb":null,"disk_size":"57M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"56.5M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":4.5,"import_time_s":null,"mem_mb":null,"disk_size":"57M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":"61.1M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":6.4,"import_time_s":null,"mem_mb":null,"disk_size":"61M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":1,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":null,"mem_mb":null,"disk_size":null}]},"quickstart_checks":{"last_tested":"2026-04-24","tag":null,"tag_description":null,"results":[{"runtime":"python:3.10-alpine","exit_code":1},{"runtime":"python:3.10-slim","exit_code":1},{"runtime":"python:3.11-alpine","exit_code":1},{"runtime":"python:3.11-slim","exit_code":1},{"runtime":"python:3.12-alpine","exit_code":1},{"runtime":"python:3.12-slim","exit_code":1},{"runtime":"python:3.13-alpine","exit_code":1},{"runtime":"python:3.13-slim","exit_code":1},{"runtime":"python:3.9-alpine","exit_code":1},{"runtime":"python:3.9-slim","exit_code":1}]}}