I have a setup.py file that checks if the user has setuptools and if he doesn't, then I am forced to use distutils. The thing is that, to make sure the submodules are installed, I use setuptools' find package:
from setuptools import setup, find_packages
packages = find_packages()
and then proceed from there.
However, I'm not sure how to do that with distutils. Is there an equivalent function or do I have to manually look for subdirs that have an __init__.py inside of them? If that is the case, is it acceptable for me to require setuptools to install my package and just forget about distutils?
Cheers.
Most Python users will not want to use this module directly, but instead use the cross-version tools maintained by the Python Packaging Authority. In particular, setuptools is an enhanced alternative to distutils that provides: support for declaring project dependencies.
Even in a vanilla version of Python 3.7. 6 (installed via pyenv ), the packages installed by default are both pip and setuptools .
distutils has been deprecated in NumPy 1.23. 0 . It will be removed for Python 3.12; for Python <= 3.11 it will not be removed until 2 years after the Python 3.12 release (Oct 2025).
Function defined in setuptools find_packages(where='.', exclude=()) Return a list all Python packages found within directory 'where' 'where' should be supplied as a “cross-platform” (i.e. URL-style) path; it will be converted to the appropriate local path syntax.
It is perfectly acceptable to use setuptools; the vast majority of packages on PyPI already do.
If you want to re-invent the find_packages() wheel, then yes, look for directories with __init__.py files in them. This is what the setuptools.PackageFinder class does. A simplified re-implementation would be:
import os
from distutils.util import convert_path
def find_packages(base_path):
base_path = convert_path(base_path)
found = []
for root, dirs, files in os.walk(base_path, followlinks=True):
dirs[:] = [d for d in dirs if d[0] != '.' and d not in ('ez_setup', '__pycache__')]
relpath = os.path.relpath(root, base_path)
parent = relpath.replace(os.sep, '.').lstrip('.')
if relpath != '.' and parent not in found:
# foo.bar package but no foo package, skip
continue
for dir in dirs:
if os.path.isfile(os.path.join(root, dir, '__init__.py')):
package = '.'.join((parent, dir)) if parent else dir
found.append(package)
return found
This ignores the include and exclude arguments of setuptools.find_packages().
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With