Every Python Programmer Should Thank It! Without It, There Would Be No Flourishing Python Open Source Ecosystem!

Introduction

Hello everyone, I am Stubborn Bronze III. Welcome to follow me on my WeChat public account: Stubborn Bronze III. Please like, bookmark, and follow, a triple click!!!

You may have never heard of <span>distutils</span>, but it is this ancient module, obscured by modern toolchains, that underpins the initial foundation of the entire Python open source ecosystem.

From 1998 to today, it has no glamorous interface, no marketing hype, yet it has enabled millions of developers to easily share code— it is the true silent hero of the Python ecosystem.

The Birth of distutils: The First Standardization Attempt in Python Open Source

In the late 1990s, the Python community was experiencing explosive growth, but the distribution and installation of packages were chaotic.

Developers manually packaged, manually copied files, manually modified sys.path, and some even uploaded .tar.gz files via FTP for users to unpack and install themselves. This primitive method severely hindered code sharing and collaboration.

In 1998, Python core developer Greg Ward submitted the distutils module, which provided a standardized mechanism for packaging, distributing, and installing Python for the first time. It was integrated into Python 2.0 (released in 2000) and became part of the official standard library.

The emergence of distutils meant that developers only needed to write a setup.py file to define the package’s metadata, dependencies, and build instructions, achieving the goal of “write once, install anywhere”.

It is not perfect, but it is the first true package management solution for Python.

The Core Role of distutils: The Triple Engine of Build, Distribution, and Installation

The core functionality of distutils revolves around three key operations:

  1. Build: Compiling source code into executable modules, including compiling C extensions;
  2. Package (sdist): Generating source distribution packages (.tar.gz);
  3. Install: Installing the package into the site-packages directory of the Python environment.

It does not handle dependency resolution, manage version conflicts, or support virtual environments—these are the responsibilities of later tools. But at the time, it solved the most fundamental problem: enabling Python packages to be “distributable”.

A typical setup.py file is as follows:

from distutils.core import setup

setup(
    name='example-package',
    version='1.0.0',
    description='A simple example package using distutils',
    author='Anonymous Developer',
    author_email='[email protected]',
    py_modules=['example'],
    classifiers=[
        'Programming Language :: Python :: 3',
        'License :: OSI Approved :: MIT License',
        'Operating System :: OS Independent',
    ],
)

Here, <span>py_modules=['example']</span> indicates that the package contains a module named example.py. The developer only needs to run the following commands in the terminal:

python setup.py sdist
python setup.py install

to generate the distribution package and complete the installation. Although simple, it was sufficient to support the early operation of PyPI (Python Package Index).

The Golden Age of distutils: The Foundation of PyPI

In 2003, PyPI went online, becoming the central repository for Python packages worldwide. Almost all packages uploaded to PyPI relied on the source distribution packages (sdist) generated by distutils. The early success of PyPI was built on the standardized format provided by distutils.

Without distutils, there would be no foundation for the birth of pip. Without pip, there would be no flourishing modern Python ecosystem. Pip was born in 2008, virtualenv became popular in 2012, and pyproject.toml emerged in 2016—these tools were all built on the framework of distutils. It is like a foundation, invisible yet supporting the entire building.

The Twilight of distutils: Inevitable Replacement and Irreplaceable Legacy

As the Python ecosystem became more complex, the limitations of distutils became increasingly apparent:

  • Inability to declare dependencies (e.g., numpy>=1.18);
  • No support for build hooks;
  • C extension compilation configuration is clumsy;
  • Lack of native support for modern build systems (like wheel).

In 2018, the Python Packaging Authority (PyPA) officially declared distutils a “legacy system” and recommended using setuptools, wheel, and pip as replacements. In 2020, PEP 632 explicitly stated that distutils would be removed in Python 3.12 (which was officially removed in Python 3.12 in 2023).

However, its legacy is everywhere:

  • setuptools still uses distutils as a base module (it will gradually decouple until 2024);
  • All old setup.py files are still operational;
  • A large number of legacy projects depend on the distutils API;
  • The metadata structure of the wheel format still follows the specifications defined by distutils.

How to Safely Use distutils in Modern Environments

Although distutils has been marked as deprecated, it is still necessary to understand its usage when maintaining old projects or learning the history of Python package management. Here is a complete, standalone example suitable for Python 3.8–3.11 environments:

# setup.py
from distutils.core import setup
from distutils.extension import Extension
import os

# Check if C extension compilation is supported
try:
    from distutils.command.build_ext import build_ext
except ImportError:
    build_ext = None

# Define a simple C extension module (optional)
if build_ext:
    ext_modules = [
        Extension(
            name='example_ext',
            sources=['example_ext.c'],
        )
    ]
else:
    ext_modules = []

# Create a simple Python module
with open('example.py', 'w') as f:
    f.write('def greet():\n    return "Hello from distutils!"\n')

# Create C extension source (only if the compiler is available)
if build_ext:
    with open('example_ext.c', 'w') as f:
        f.write('#include <Python.h>\n\n'
                'static PyObject* hello(PyObject* self, PyObject* args) {\n'
                '    return PyUnicode_FromString("Hello from C!");\n'
                '}\n\n'
                'static PyMethodDef methods[] = {\n'
                '    {"hello", hello, METH_NOARGS, "Returns a C string."},\n'
                '    {NULL, NULL, 0, NULL}\n'
                '};\n\n'
                'static struct PyModuleDef moduledef = {\n'
                '    PyModuleDef_HEAD_INIT,\n'
                '    "example_ext",\n'
                '    NULL,\n'
                '    -1,\n'
                '    methods\n'
                '};\n\n'
                'PyMODINIT_FUNC PyInit_example_ext(void) {\n'
                '    return PyModule_Create(&moduledef);\n'
                '}\n')

# Set package information
setup(
    name='legacy-distutils-demo',
    version='0.1.0',
    description='A minimal demo using distutils (Python < 3.12)',
    author='Historical Developer',
    py_modules=['example'],
    ext_modules=ext_modules,
    classifiers=[
        'Programming Language :: Python :: 3',
        'License :: OSI Approved :: MIT License',
        'Topic :: Software Development :: Libraries :: Python Modules',
    ],
)

Run the commands (in Python versions 3.11 and below):

python setup.py sdist

# Install to the current environment
python setup.py install

# Verify installation
python -c "import example; print(example.greet())"

Note: Running the above code in Python 3.12+ will raise an error <span>ModuleNotFoundError: No module named 'distutils'</span>. At this point, you should migrate to setuptools:

# Alternative: Use setuptools (recommended)
from setuptools import setup, find_packages

setup(
    name='modern-package',
    version='1.0.0',
    packages=find_packages(),
    python_requires='>=3.8',
)

The Lessons from distutils: The Underlying Logic of the Open Source Ecosystem

The rise and fall of distutils reveal deep laws of open source ecosystem development:

  • The greatest tools are often the simplest;
  • Standardization is more important than innovation;
  • A simple interface can leverage an entire ecosystem;
  • Removing old technology is not betrayal, but evolution.

It has not been widely celebrated like pip, nor does it have the community adoration of poetry, but it has allowed Python to transition from a “scripting language” to an “industrial-grade language”. Every package you can install with one click today once relied on that setup.py file it generated.

Conclusion: A Tribute to the Silent Founders

distutils is dead, but its spirit lives on. When we use pip install to depend on packages, we are not just using a tool, but enjoying a collaborative miracle that has lasted over twenty years.

distutils is the first brick of this miracle; it is not flashy, nor loud, but it has made the world hear the voice of Python.

Have you ever used <span>distutils</span> to package a dependency? What is your evaluation of it? Feel free to share in the comments!

Finally, thank you for reading! Please follow me on my WeChat public account: Stubborn Bronze III. Please like, bookmark, and follow, a triple click!! Feel free to click the [👍 Like the Author] button to donate💰💰 and buy me a cup of coffee☕️!

Practicing Python Day 91: Stop just using import! Python’s built-in importlib is the key to truly mastering modules.Why modern Python programmers must learn Pydantic? A revolutionary tool from data validation to API development.

Leave a Comment