Of course! This is a great question because it gets to the heart of how Python packaging has evolved. The short answer is:

Yes, you absolutely can and should create and install Python packages without setuptools.
While setuptools was the long-standing, historical standard, the modern, official standard is defined by the Python Packaging Authority (PyPA) and is built around a new set of tools. The goal is to simplify the packaging process and make it more consistent.
Let's break down the old way, the new way, and how to install packages without either.
The Old Way: The setuptools Era (The "Legacy" Method)
For many years, setuptools was the king. You would create a setup.py file that looked something like this:

setup.py
from setuptools import setup, find_packages
setup(
name="my-awesome-package",
version="0.1.0",
packages=find_packages(),
install_requires=[
"requests",
"numpy",
],
author="Your Name",
author_email="your.email@example.com",
description="A very cool package.",
url="https://github.com/yourusername/my-awesome-package",
)
To build your package, you would run:
# Create a source distribution (a .tar.gz file) python setup.py sdist # Create a binary distribution (a .whl file) python setup.py bdist_wheel
Why move away from this?
- Complexity:
setup.pycan become very complex. - Reproducibility: It was hard to guarantee that
setup.pywould work the same way across different environments. - Security:
setup.pycan execute arbitrary code, which is a security risk.
The Modern Way: The pyproject.toml Standard (PEP 517 & PEP 518)
This is the current, recommended approach. Instead of a setup.py script, you define your project's metadata and build requirements in a simple, declarative [TOML](https://toml.io/) file named pyproject.toml.

This new system separates metadata (pyproject.toml) from build logic (handled by a "build backend").
Key Tools in the Modern Ecosystem:
build: The official tool to build your package frompyproject.toml. It replacespython setup.py sdist bdist_wheel.flitorhatchling: These are modern "build backends". They read yourpyproject.tomland do the actual work of building the source and wheel files. You only need to install one of them.pip: Modernpip(version 10+) is the standard for installing packages from these build artifacts.
Example: Creating a Package with pyproject.toml (No setuptools)
Let's create a simple package called greet without touching setuptools.
Step 1: Project Structure
Create a directory structure like this:
my_project/
├── pyproject.toml
├── src/
│ └── greet/
│ ├── __init__.py
│ └── greeter.py
└── README.md
Step 2: Write the Code
src/greet/__init__.py
from .greeter import Greeter __version__ = "0.1.0"
src/greet/greeter.py
class Greeter:
def __init__(self, name):
self.name = name
def say_hello(self):
return f"Hello, {self.name}!"
Step 3: Create pyproject.toml
This is the core of the modern packaging. We'll use hatchling as the build backend.
pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "greet"
version = "0.1.0"
authors = [
{ name="Your Name", email="you@example.com" },
]
description = "A simple package that says hello"
readme = "README.md"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
]
dependencies = [
"rich", # A nice dependency to make our output colorful
]
[project.urls]
Homepage = "https://github.com/yourusername/greet"
Issues = "https://github.com/yourusername/greet/issues"
Step 4: Build the Package
Now, use the build tool to create your distribution files (.tar.gz and .whl).
-
Install the build tool:
pip install build
-
Run the build command in your project's root directory (
my_project/):python -m build
You will see output like this and new files will appear in a dist/ directory:
* Getting dependencies for wheel...
...
* Building wheel for greet (pyproject.toml)
...
Successfully built greet-0.1.0-py3-none-any.whl and greet-0.1.0.tar.gz
You have now successfully built a Python package without setuptools!
Step 5: Install Your Package
You can install your built package using pip.
# Install the wheel file for the current user pip install dist/greet-0.1.0-py3-none-any.whl --user
Now you can use it in any Python script:
>>> from greet import Greeter
>>> greeter = Greeter("World")
>>> greeter.say_hello()
'Hello, World!'
Installing Packages Without setuptools (The End-User)
This is a different but related question. As an end-user, you don't need to install setuptools to install most packages from PyPI.
The tool you use is pip.
When you run pip install some-package, pip does the following:
- It downloads the package's metadata from PyPI.
- It looks at the package's
pyproject.toml(if it's a modern package) orsetup.py(if it's a legacy package). - It uses a temporary, isolated build environment to compile and build the package.
- It installs the built result into your Python environment.
pip handles the complexity. It uses a temporary build backend (like build or setuptools itself) to do the work, so your global environment stays clean. You only need setuptools installed if you are the one developing a package that uses it.
Summary: setuptools vs. Modern Tools
| Feature | setuptools (Legacy) |
Modern (pyproject.toml) |
|---|---|---|
| Core File | setup.py (Python script) |
pyproject.toml (TOML config file) |
| Build Tool | python setup.py sdist bdist_wheel |
python -m build |
| Build Backend | setuptools itself |
hatchling, flit, pdm, etc. |
| Philosophy | Imperative ("do this, then do that") | Declarative ("my project is this") |
| Status | Still works, but considered legacy. | Official standard (PEP 517/518). |
| Dependency | Required for development. | Not required for development if using a non-setuptools backend. |
Conclusion:
For any new project, you should use the pyproject.toml standard. It is simpler, more secure, and is the future of Python packaging. You can build powerful, distributable Python packages without ever installing setuptools on your development machine.
