To configure package discovery for a Python project using the src layout on macOS, point setuptools at src in pyproject.toml, then set package-name filters only if you need to control which packages ship. For a conventional project, setuptools can often detect the layout automatically; explicit discovery settings are useful when you want predictable inclusions or exclusions. These instructions apply to setuptools, not every Python build backend.
Check the project structure and build backend
From Terminal, change to the project root—the folder containing pyproject.toml—and inspect the files with macOS’s default shell, zsh, or with bash if that is your configured shell:
cd '/path/to/project'
find src -maxdepth 3 -type f -print
Replace the quoted path with the actual project location. Single quotes keep spaces and shell metacharacters in a path from being interpreted. A typical package might be located at src/my_package/__init__.py, with subpackages in directories below it. The import name is my_package; the directory called src is only a container and is not part of that name.
Check that pyproject.toml selects setuptools under [build-system], for example with requires = ["setuptools>=61"] and build-backend = "setuptools.build_meta". The setuptools documentation describes automatic discovery for common layouts, including src, when package and module lists are not explicitly supplied. Other backends have their own configuration, so do not copy setuptools settings into another backend’s configuration table. Setuptools: Configuring setuptools using pyproject.toml files
Configure explicit discovery in pyproject.toml
If you want discovery rooted specifically in src, add the following setuptools configuration to the existing file:
[tool.setuptools.packages.find]
where = ["src"]
This tells setuptools where to look for packages. It does not change the project’s name or the package’s import name. If you already have a [tool.setuptools.packages.find] table, edit that table rather than adding a second one. TOML files cannot contain duplicate definitions of the same table.
To restrict discovery to one package and its subpackages, add an include pattern matching the full dotted package name:
[tool.setuptools.packages.find]
where = ["src"]
include = ["my_package*"]
Replace my_package with the actual import package name. The * allows the pattern to match subpackages such as my_package.tools; patterns are matched against full package names. You can also exclude selected packages, for example exclude = ["my_package.tests*"]. Setuptools documents these include and exclude patterns, as well as where, in its package discovery guide.
Choose how discovery handles namespace packages
By default, setuptools’ pyproject.toml discovery scans implicit namespace packages: directories can be treated as packages even when they do not contain __init__.py. Keep this default if your package intentionally uses PEP 420 namespace packages. If every package should have an __init__.py, you can turn off namespace scanning:
[tool.setuptools.packages.find]
where = ["src"]
namespaces = false
With this setting, directories without __init__.py are not discovered as packages. A missing package in the built distribution may therefore be explained by either the search location, an include or exclude pattern, or the namespace setting. The packaging guide discusses this behavior and shows how discovery can be limited to selected namespace packages. Python Packaging User Guide: Packaging namespace packages
Know when automatic discovery is enough
For a straightforward src/my_package project, you may not need a discovery table at all. Setuptools can recognize common layouts when packages and py-modules are left unspecified. Add explicit configuration when you need to narrow package selection, handle a less conventional tree, or make the source directory unambiguous. Avoid separately hard-coding package lists unless you intend to maintain them as the package tree changes; explicit lists can make newly added subpackages easy to omit.
A src layout also means running Python from the repository root does not automatically put the package on the import path. That is expected: setuptools’ documentation notes that an editable install is needed to use the package from a source checkout in this way. Discovery config controls what gets included in a distribution; it is not a substitute for installing the project into the Python environment you use for development. Setuptools: Package Discovery and Namespace Packages
Sources
Configuring setuptools using pyproject.toml files — setuptools
Package Discovery and Namespace Packages — setuptools
Packaging namespace packages — Python Packaging Authority