Python

[languages.python]                  # off unless the table is written
include = ["**/*.py", "**/*.pyi"]   # the default
roots = ["services/api/src", "."]   # found from the packages unless given
ignore_unresolved = ["generated.*"]  # module paths not to warn about
Option
includeGlobs of the files to scan.
rootsThe directories that absolute imports start from, relative to the project root, as on sys.path.
ignore_unresolvedName patterns of the module paths that are expected not to resolve, so are not reported as unresolved.

Packages

Every pyproject.toml that names a package, in its [project] table or in Poetry's [tool.poetry], is a package, which pkg: selects by that name, so pkg:acme-* selects the packages whose names start with acme-. A pyproject.toml that only configures tools, or a uv workspace, is not a package.

Roots

An absolute import, such as import acme.api, starts from a root, as it would from a directory on sys.path. Unless roots names them, the roots are the src directory of each package in the src layout, or the package's own directory otherwise, and then the project root. The roots that hold the importing file come first.

A file that no root reaches, such as a script run from its own directory or a test that pytest imports, also starts from the directory above its outermost package: the nearest directory above it without an __init__.py. So scripts/migrate.py can import helpers from scripts/helpers.py.

Imports

import a.b imports a.b, and from a import b imports b from a, which is the module a.b if there is one, and the name b in a otherwise. from a import * imports a, and a lazy import, such as lazy import a, is an import like any other. Relative imports, such as from . import b and from ..c import d, start at the importing file's package. from __future__ imports nothing.

ImportKind
import, from … importvalue, wherever it is, in a function or a try as at the top of the file
an import in an if TYPE_CHECKING: blocktype
importlib.import_module("a.b"), __import__("a")dynamic

A call of importlib.import_module or __import__ whose module is not a string literal, or is relative, can't be resolved, and is counted as a non-literal import. zenith-ignore comments work as # comments.

Resolution

A module path leads through the packages to the file of the last module on it, and what follows is names in that module. A regular package is its __init__.py, and a module is its .py file, or its .pyi stub when it has no source. A directory without an __init__.py is a namespace package, which leads on to the modules in it. Names are compared exactly, even where the file system ignores case.

ImportedNode
a module of the projectservices/api/src/acme/api/handlers.pyIts file. from . import * in a namespace package is its directory, and any other name in one that is not a module doesn't resolve.
the standard librarypython:osNamed by its top-level module.
anything elsepypi:requestsNamed by its top-level module, which is usually, but not always, the name of the distribution: import yaml is pypi:yaml, from PyYAML.

A module or a regular package of the project hides a standard library module of the same name, as it does when Python runs, but a namespace package does not, so a directory called email doesn't hide email. A file doesn't import itself.