Introduction

Setuptools is deprecating the legacy setup.py-only packaging format in favor of PEP 517/518 pyproject.toml-based builds. While existing setup.py files still work, they trigger warnings and future versions of pip will refuse to use them. Projects need to migrate to pyproject.toml for forward compatibility.

Symptoms

  • WARNING: Running setup.py install for package is deprecated. Use pip install with PEP 517 builds.
  • DEPRECATION: package is being installed using the legacy 'setup.py install' method
  • UserWarning: setup.py install is deprecated. Use build
  • pip install output shows: Preparing metadata (setup.py) ... done instead of Preparing metadata (pyproject.toml)
bash
DEPRECATION: mypackage is being installed using the legacy
'setup.py install' method because it does not have a
'pyproject.toml' and the wheel package is not installed.

Common Causes

  • Project uses setup.py with setup() call but no pyproject.toml
  • pyproject.toml exists but lacks [build-system] table
  • Using python setup.py sdist bdist_wheel instead of python -m build
  • Old project templates not updated for modern packaging

Step-by-Step Fix

  1. 1.Create minimal pyproject.toml:
  2. 2.```toml
  3. 3.[build-system]
  4. 4.requires = ["setuptools>=68.0", "wheel"]
  5. 5.build-backend = "setuptools.build_meta"

[project] name = "my-package" version = "1.0.0" description = "A useful Python package" readme = "README.md" requires-python = ">=3.10" license = {text = "MIT"} authors = [ {name = "Your Name", email = "you@example.com"}, ] dependencies = [ "requests>=2.28.0", "click>=8.0", ]

[project.optional-dependencies] dev = ["pytest>=7.0", "black>=23.0"]

[project.scripts] my-cli = "my_package.cli:main" ```

  1. 1.Migrate setup() arguments to pyproject.toml:
  2. 2.```python
  3. 3.# OLD: setup.py
  4. 4.from setuptools import setup, find_packages

setup( name="my-package", version="1.0.0", packages=find_packages(exclude=["tests"]), install_requires=["requests>=2.28.0", "click>=8.0"], python_requires=">=3.10", entry_points={ "console_scripts": ["my-cli=my_package.cli:main"], }, )

# NEW: Remove setup.py entirely or keep minimal: # setup.py (only if you need legacy compatibility) from setuptools import setup setup() # Reads from pyproject.toml ```

  1. 1.Migrate package data and include/exclude rules:
  2. 2.```toml
  3. 3.[tool.setuptools.packages.find]
  4. 4.where = ["src"]
  5. 5.exclude = ["tests*"]

[tool.setuptools.package-data] my_package = ["data/*.json", "templates/*.html"]

[tool.setuptools] include-package-data = true ```

  1. 1.Build using modern tools:
  2. 2.```bash
  3. 3.# Install build tool
  4. 4.pip install build

# Build wheel and source distribution python -m build

# Verify no deprecation warnings pip install --no-cache-dir dist/my_package-1.0.0-py3-none-any.whl ```

  1. 1.Migrate entry points and scripts:
  2. 2.```toml
  3. 3.[project.scripts]
  4. 4.my-cli = "my_package.cli:main"

[project.gui-scripts] my-gui = "my_package.gui:main"

[project.entry-points."myplugin.hooks"] hook_a = "my_package.plugins:hook_a" ```

Prevention

  • Use pip check to verify packaging works without warnings
  • Adopt hatch or pdm as modern build backends
  • Run twine check dist/* before publishing
  • Use check-manifest to verify all files are included
  • Set up CI to catch deprecation warnings early:
  • ```bash
  • python -W error::DeprecationWarning -m build
  • `