Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Why does `distutils` seem to be missing or broken? Isn't it part of the standard library?

+0
−0

Sometimes when I try to install a third-party library for Python, I get an error saying that either distutils, or some part of it like distutils.core or distutils.util, could not be found. It's shown as a ModuleNotFoundError, so presumably it's coming from Python when it tries to run a setup.py script for installation.

I've also seen similar messages when using libraries that were already installed. For example, if I try installing the popular SpeechRecognition third-party library in Python 3.12, I can reproduce this bug report:

Python 3.12.2 (main, Mar 29 2024, 00:26:08) [GCC 9.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import speech_recognition as sr
>>> sr.Microphone()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/lib/python3.12/site-packages/speech_recognition/__init__.py", line 80, in __init__
    self.pyaudio_module = self.get_pyaudio()
                          ^^^^^^^^^^^^^^^^^^
  File "/path/to/lib/python3.12/site-packages/speech_recognition/__init__.py", line 111, in get_pyaudio
    from distutils.version import LooseVersion
ModuleNotFoundError: No module named 'distutils'

Why do problems like this occur? Isn't distutils part of the standard library?

I've heard some suggestions that installing setuptools can fix the problem. Why and how does this work?

History
Why does this post require moderator attention?
You might want to add some details to your flag.
Why should this post be closed?

0 comment threads

1 answer

+1
−0

Understanding distutils and setuptools: the history

The short version of "why does installing Setuptools fix the problem?" is that Setuptools provides its own distutils package, overriding anything the standard library might or might not have. When you import setuptools explicitly, it furthermore ensures that a patched version of distutils is imported.

Way back in the prehistory of Python, the Distutils package was created so that there would be some standard way of distributing and installing each others' Python code - including compiling C extensions, with some nice wrappers to deal with other computers having different C compilers available. It was made available when Python 1.5 was current, and became part of the standard library in 1.6.

However, over time a lot of weaknesses were exposed in this system, and people also started to suspect that relying on the standard library in this particular way might be a mistake. Multiple third-party alternatives popped up, and Setuptools won out. To this day, Setuptools is still technically third-party.

For backwards-compatibility reasons, Setuptools worked by patching the standard library distutils package, in addition to providing its own modules.

Starting around Python 3.4, using Distutils directly became "informally deprecated"; the Python documentation started to recommend using Setuptools instead, and moved the actual information about how to use Distutils into a separate legacy documentation section. In Python 3.10 the deprecation became official; and following the usual deprecation policy, the distutils package was removed from the standard library in Python 3.12. Since Setuptools can no longer depend on the standard library including distutils at all, it now includes its own "vendored" version of the old distutils, and applies its own modifications on top of that.

Fixing problems with others' code, in Python 3.12

To fix problems running code that relies on distutils, install Setuptools in the environment where the code will run.

If you have a problem installing code on 3.12 because of distutils, the situation is a lot more complicated.

Usually there is no problem with installing a library that relies on distutils. Ideally, the library was pre-built into a wheel, so all the parts that rely on distutils have been done ahead of time. If that didn't happen (which means Pip will try to install from source, unless told to give up), the library is supposed to be responsible for specifying build requirements. Newer projects that use a pyproject.toml file can explicitly say that the build environment should contain setuptools, and that will make Pip install Setuptools first. When the pyproject.toml doesn't say what "build system" to use, Setuptools is supposed to be the default anyway.

However, in Python 3.12, circumstances can combine to create a very annoying problem. Pip tries to use build isolation when it installs from source, meaning that it creates a separate virtual environment for the installation process (so that, for example, the setup process can use specific versions of Setuptools and other such tools, even if the code will be installed into an environment with a different version of those tools). Starting with Python 3.12, virtual environments created using the venv standard library (like how Pip does it) don't include Setuptools by default. This sometimes causes installations to fail: even though Setuptools is installed in the Python where we want to install the new library, it isn't installed in the Python where it will be built.

In these cases, you can try passing the --no-build-isolation flag to Pip, to make it use the Setuptools (and potentially any other previously installed libraries!) from the existing environment while trying to install the new library. If that doesn't work, the only remaining option is to file a bug for that library (or check if the author has already said anything about it).

Fixing problems with others' code, in earlier Python versions

If distutils.core, distutils.util etc. seem to be missing on your system, especially on Linux, this is probably because you are using a copy of Python that came with the system that has these components disabled. Your best bet is to use a virtual environment and make sure Setuptools is installed in that virtual environment (this should happen automatically by default). Otherwise, you can check with your distro to see if there is a system-level package you can install to add Distutils to your system Python.

Be aware of the usual risks involved in modifying a program that came with your operating system - especially since this is taking a step towards making it possible to add other third-party code to your system Python directly, bypassing the system package manager. (For the same reason, your system Python might not include Pip.) If someone managed to get malware onto PyPI and you inadvertently installed it this way, it could result in serious damage. Even ordinary third-party packages could potentially interfere with critical system Python scripts.

Fixing problems with your own code

If you must maintain code that depends on distutils functionality and want it to work in Python 3.12 (especially if you are distributing it to others), consider replacing uses of distutils that can be covered by other standard library modules. If you still have parts for which "setuptools is the best substitute":

  • Install Setuptools locally

  • If your code uses the functionality at runtime, make sure that Setuptools is listed as a runtime dependency - typically, by mentioning it in the project.dependencies in pyproject.toml.

  • If at all possible, build wheels locally and distribute them as well, even if your project uses only Python source code.

  • If you must distribute source that must use Distutils during the installation process, make sure your project has a pyproject.toml (add one, if it didn't already) that explicitly lists Setuptools as a build-time dependency, by describing it in the [build-tool] table. If applicable, modify your setup.py so that setup gets imported from Setuptools rather than Distutils (i.e., replace from distutils.core import setup with from setuptools import setup).

History
Why does this post require moderator attention?
You might want to add some details to your flag.

0 comment threads

Sign up to answer this question »