Skip to content

IECQ004: external_dependencies

Third-party imports are declared in pyproject.toml.

Category: code_quality ยท Applies to: eval, helper

What it does

Collects every import in the package and treats one as external when it is not in the standard library, not in [project].dependencies or something those dependencies require in turn, not local to the package and not one of the repository's own packages (the import-prefix package, and every package under source-root such as a utils helper). The transitive requirements are read from uv.lock when the repository commits one, else from the distributions installed in the current environment; with neither, only the declared names count and the hint on an undeclared import says so. For an evaluation, each external import must be declared in some [project.optional-dependencies] group or [dependency-groups] entry (other than dev), or in the isolated package's pyproject.toml when isolated-packages-dir is set. With per-eval-dependency-group = true (the monorepo preset) the evaluation must also own a group named after itself unless it is isolated, so one evaluation's dependencies can be installed without the rest; a standalone repository declares its dependencies in [project].dependencies and any extra it likes.

For a helper package the rule is different, because every evaluation that imports the helper loads whatever it imports at module level: those imports must be in [project].dependencies, while imports inside a function, a try block or an if TYPE_CHECKING: block are deferred and only need to be declared in some group or in any isolated package. Helpers need no group of their own. One diagnostic per import, at its first site.

Import-to-distribution mapping uses the packages installed in the current environment plus a few static aliases, so run the linter inside the project's environment. Names are compared in PEP 503 normalised form.

Why is this bad?

An import nobody declared works on the author's machine and fails for the next person with ModuleNotFoundError, often only when a particular sample runs.

Example

A standalone repository:

[project]
dependencies = ["inspect_ai", "datasets>=4.0"]

[project.optional-dependencies]
modal = ["inspect_sandboxes"]

The inspect_evals monorepo, with per-eval-dependency-group = true:

[project.optional-dependencies]
my_eval = ["datasets>=4.0", "scikit-learn"]

Options

  • per-eval-dependency-group
  • isolated-packages-dir
  • import-prefix

See also

Suppress on a line with # inspect-evals-lint: ignore[IECQ004] -- <reason> or ignore[external_dependencies] -- <reason>; select or ignore it in configuration by either, or by the prefix IECQ.