Configuration¶
license-audit is configured via [tool.license-audit] in your pyproject.toml.
Configuration is loaded from the target's location: a project directory uses its own pyproject.toml, and a virtualenv uses the pyproject.toml beside it. Use --config to read config from elsewhere, which is handy when the virtualenv lives outside your project. If no config is found, defaults apply.
Options¶
fail-on-unknown¶
Whether the check command fails when a dependency has an undetectable license. Default: true.
policy¶
License policy preset. Default: "permissive".
| Value | Description |
|---|---|
"permissive" |
Only allow permissive licenses (MIT, BSD, Apache, etc.) |
"weak-copyleft" |
Allow permissive + weak copyleft (LGPL, MPL, etc.) |
"strong-copyleft" |
Allow permissive + weak + strong copyleft (GPL, etc.) |
"network-copyleft" |
Allow all open-source licenses including AGPL |
Each preset sets the maximum copyleft level allowed; anything above the threshold fails the check.
The --policy CLI flag overrides this setting:
allowed-licenses¶
Explicit list of allowed SPDX identifiers. When set, only these licenses pass the policy check, narrowing whatever policy would otherwise allow.
denied-licenses¶
SPDX identifiers that always fail the policy check, regardless of policy or allowed-licenses.
Choosing dependency groups¶
Selecting which dependency groups to audit happens when you provision: install only the groups you care about, then audit that environment. For example, uv sync --no-dev for a production-only audit, or uv sync --all-groups to include everything.
target¶
Default --target to use when none is supplied on the CLI. Relative paths resolve against the directory containing pyproject.toml.
The CLI --target flag overrides this setting:
When target is unset and --target is omitted, license-audit falls back to analyzing the active Python environment (see "Target resolution" below).
overrides¶
Manual license assignments for packages where auto-detection fails.
Values must be valid SPDX license expressions. Common aliases are normalized to their canonical SPDX form ("apache" becomes Apache-2.0); a value that can't be normalized is rejected at config load. For a license with no SPDX equivalent, use license-classifications instead.
ignored-packages¶
Exempt specific packages from policy evaluation. Each entry is a reason string that ends up in the audit trail.
[tool.license-audit.ignored-packages]
pandas-stubs = "Stubs only, not redistributed"
internal-tool = "Vendored, excluded from dist"
Ignored packages are skipped by check's policy evaluation (no exit 1 or 2), excluded from incompatible-pair analysis (so they don't constrain recommendations), and still listed in every report (terminal, markdown, JSON, notices) with an ignored marker plus the reason (notices shows the marker only).
The reason is required and must be non-empty; empty reasons are rejected at config load. Package names are canonicalized per PEP 503, so pandas-stubs, pandas_stubs, and Pandas.Stubs all match.
Use overrides when you want to re-assert what the license is. Use ignored-packages when the license is correct but doesn't apply to your situation (e.g. the package isn't shipped, or you've reviewed it manually and accepted the risk).
license-classifications¶
Record your own judgement about a license and apply it to every package that uses that license. This is useful for custom strings detection can't map to SPDX, for a valid SPDX license the OSADL data doesn't classify (CNRI-Python, say, which otherwise reports as unknown), and for a recognized license you've reviewed (for example, MPL-2.0 dependencies you distribute unmodified, which you consider permissive). Unlike overrides (keyed by package name), one entry here applies to all matching packages.
[tool.license-audit.license-classifications]
"MPL-2.0" = "permissive"
"Proprietary License" = "proprietary"
The value must be one of permissive, weak-copyleft, strong-copyleft, network-copyleft, or proprietary. (unknown is rejected, since the point is to resolve an unknown.) The key is matched case- and whitespace-insensitively against the license string shown in reports: a recognized SPDX expression like MPL-2.0, or the raw declared string when the license couldn't be mapped to SPDX. A package with no detectable license at all isn't matched, so "UNKNOWN" as a key matches nothing.
A key also matches a component inside an AND/OR expression. The deemed category is substituted for that component and the expression is re-evaluated under normal AND/OR rules: deeming MPL-2.0 permissive makes MPL-2.0 AND MIT permissive (both parts are now permissive), while deeming it proprietary makes the whole expression proprietary (an AND keeps the most restrictive part). For OR, the most permissive alternative still wins, so deeming one branch permissive makes the expression permissive. A WITH clause is one component, keyed by its full text, so classify Apache-2.0 WITH LLVM-exception as a single entry. A matched component is also dropped from compatibility analysis, so the waiver is consistent. When a key matches neither a whole license nor any component, the report adds a warning (a likely typo).
Once classified, the package is governed by the deemed category: it passes --fail-on-unknown, is evaluated against your policy by that category (so deeming something strong-copyleft still fails a permissive policy), and no longer blocks outbound recommendations. Reports tag the category with a (classified) marker to record that it's your judgement rather than detected from OSADL data.
A classified package is dropped from pairwise compatibility analysis, since you've asserted its category directly. Deeming MPL-2.0 permissive, for example, also waives any MPL incompatibility warnings. A deemed permissive license imposes no outbound constraint, so recommendations proceed normally; deeming something copyleft/proprietary leaves a constraint that is excluded from compatibility analysis, so recommendations are withheld with an explanation (the JSON report just leaves the list empty) rather than risk suggesting an incompatible outbound license.
Use license-classifications to assert a category and have it apply everywhere; use overrides when you want a license treated as a genuine SPDX equivalent and kept in full compatibility analysis.
Target resolution¶
license-audit always reads an installed environment. Provision your dependencies first (uv sync, poetry install, pip install -e ., ...), then point license-audit at the result. --target selects the environment:
| Target | Behavior |
|---|---|
| (none) | Audit ./.venv if present, otherwise the Python environment running license-audit |
| Project directory | Audit <dir>/.venv (errors if there is no virtualenv to read) |
| Virtualenv directory | Audit that virtualenv directly |
| A file | Rejected; point at a project directory or virtualenv instead |
--config decouples where config and the project name come from. By default they follow the target (a project directory, or a virtualenv's parent); pass --config path/to/pyproject.toml to override, which is useful when the virtualenv lives outside your project.
Examples:
license-audit analyze # ./.venv, else the active environment
uv run license-audit analyze # the project's own environment
license-audit --target /path/to/project analyze # that project's .venv
license-audit --target /path/to/.venv analyze # an existing virtualenv
license-audit --target /ext/.venv --config . analyze # external venv, this project's policy