Skip to main content

trustedlicenses

CI PyPI version Python versions Documentation License: MIT

⚠️ Project Status: Experimental

trustedlicenses (v0.3.2) is an early, experimental release. Detection, policy evaluation, and the CLI work end-to-end with full test coverage, but the API and config format aren't stable yet, and license detection — declared-metadata parsing and the Rust text-matching fallback alike — can be wrong. See the legal disclaimer before relying on this for a real compliance decision.

trustedlicenses checks that every package installed in your Python project has a license you've agreed to allow, so you can gate a CI build on it. Point it at your environment, tell it which kinds of license are acceptable, and it tells you which packages don't comply and why.

Quick look

Nothing to configure first — just run it. With no policy set up yet, a real terminal walks you through one interactively, explaining what each license category actually means as you go:

$ trustedlicenses
Detected licenses for 20 of 20 installed packages.

No policy configured yet -- would you like to run the guided setup? [Y/n]: y

Permissive: MIT, BSD, Apache-2.0, ISC, ... -- minimal restrictions: use, modify, and
redistribute freely, usually with just an attribution/copyright notice.
  (20 detected)
Allow Permissive licenses? [Y/n]: y
...
Wrote policy to pyproject.toml.

That writes a [tool.trustedlicenses] table to your pyproject.toml (or a standalone trustedlicenses.toml, your choice) — see No policy configured yet? below for the full transcript. Or skip the wizard and write it yourself:

[tool.trustedlicenses]
allowed-categories = ["Permissive", "Public Domain", "Copyleft Limited"]

Either way, running it again just checks:

uv run trustedlicenses

If everything's fine, you get a one-line pass:

Checking dependency licenses...
✓ All 134 packages passed.

If something isn't, you get exactly what's wrong, and a concrete suggestion for how to fix it:

Checking dependency licenses...
✗ Disallowed or undetectable licenses in 2 of 134 packages:
  certifi: detected MPL-2.0 (categories: Copyleft Limited) -- from declared metadata
    -> add "Copyleft Limited" to allowed-categories, or "certifi" to ignored-packages, to allow this
  fqdn: detected MPL-2.0 (categories: Copyleft Limited) -- from license files: LICENSE
    -> add "Copyleft Limited" to allowed-categories, or "fqdn" to ignored-packages, to allow this

The process exits non-zero on any failure, so it plugs straight into CI. ✓/✗ lines are green/red (and compatibility notes yellow) in a real terminal — colors are stripped automatically when output isn't a TTY (piped, redirected, NO_COLOR), exactly like ANSI color handling in most CLI tools.

Full docs: trustedlicenses.readthedocs.io

Installation

uv add --dev trustedlicenses

Usage

Add a [tool.trustedlicenses] table to your pyproject.toml (or run trustedlicenses interactively and let the wizard write it — see below):

[tool.trustedlicenses]
allowed-categories = ["Permissive", "Public Domain", "Copyleft Limited"]
ignored-packages = ["mypy-extensions"]

A standalone trustedlicenses.toml (same keys, no [tool.trustedlicenses] wrapper) works too, and takes priority if both exist.

  • allowed-categories (required, no default) — the kinds of license your project accepts. A package passes if at least one of its detected licenses falls into one of these categories. There's no default on purpose: you say what you're willing to accept, rather than inherit an assumption.
  • ignored-packages (optional) — specific packages to skip entirely, for cases you've reviewed by hand and decided are fine regardless of what's detected.

If your own project declares its license ([project.license], per PEP 639), trustedlicenses also checks it against each dependency for a small number of specific, well-documented copyleft compatibility problems — e.g. a real installed environment where a GPL-2.0-only project pulled in scipy (GPL-3.0-or-later):

i 1 compatibility note(s) -- not a pass/fail result, see below:
  scipy: your project is GPL-2.0-only; scipy is GPL-3.0-or-later -- the FSF states
  GPLv2 is not, by itself, compatible with GPLv3 (https://www.gnu.org/licenses/gpl-faq.html#AllCompatibility)

This is deliberately narrow and never affects pass/fail — see Usage Guide § compatibility notes for exactly what it does and doesn't check, and why.

Then run:

uv run trustedlicenses

This checks every package installed in the current environment. See the Usage Guide for the full category vocabulary, embedding the check in your own code, and how detection works under the hood.

No policy configured yet?

In a real terminal, running trustedlicenses with nothing configured first reports how many installed packages actually have a detectable license, then offers the interactive wizard shown above — allow/decline each of Permissive, Public Domain, Copyleft Limited, and (strong) Copyleft with an explanation for each and how many (and, for one or two, which) of your installed packages fall into it, choose pyproject.toml or a standalone trustedlicenses.toml, and it writes the config and runs the check immediately.

Without a real terminal — CI, pre-commit, piped input, or --quiet explicitly — it never prompts (that would just hang a pipeline). Instead: report-only mode, every installed package's detected license and category, no pass/fail judgment, exit code 0:

$ trustedlicenses --quiet
i pyproject.toml has no policy configured yet -- showing detected licenses only.
  babel: BSD-3-Clause (Permissive)
  certifi: MPL-2.0 (Copyleft Limited)
  jinja2: BSD-3-Clause (Permissive)
  ...

Use --quiet (-q) in CI/CD and pre-commit hooks. Both are non-interactive already, so trustedlicenses falls back on its own — but pass --quiet explicitly so that holds even if a step happens to have a terminal attached.

pre-commit

trustedlicenses audits whatever's actually installed in the current Python environment, so the hook needs to run with your project's own dependencies already installed — not in an isolated hook-specific environment the way most pre-commit hooks work. Add trustedlicenses as a dev dependency (see Installation), then reference this repo directly:

- repo: https://github.com/w-martin/trustedlicenses
  rev: v0.3.2
  hooks:
    - id: trustedlicenses

Or write the same thing as a local hook without depending on this repo's tag:

- repo: local
  hooks:
    - id: trustedlicenses
      name: trustedlicenses
      entry: trustedlicenses --quiet
      language: system
      pass_filenames: false

GitHub Actions

A composite action wraps the same install-then-run steps. Run it in the same job as your dependency install step, after your project's own dependencies are already on the Python path:

- name: Install dependencies
  run: pip install -r requirements.txt   # or uv sync, poetry install, ...

- name: Check dependency licenses
  uses: w-martin/trustedlicenses@v0.3.2

It accepts two optional inputs: version (pin the trustedlicenses release, as a pip version specifier — defaults to latest) and args (defaults to --quiet). It installs with uv pip install when uv is already on PATH, falling back to plain pip install otherwise.

As a second safety net if --quiet gets left off by mistake, every wizard prompt also times out after 30 seconds with no answer — some CI runners attach something that looks enough like a real terminal that this can't be told apart reliably, so a misconfigured job times out and falls back gracefully instead of hanging forever.

An actual misconfiguration (a config with an empty or missing allowed-categories) is always a hard error, with the exact TOML to add — never the wizard, never the report-only fallback.

Checking a package before you add it

trustedlicenses check <package> [<package> ...] resolves the package(s) — and every transitive dependency — into an isolated temporary location, and checks the whole set against your project's policy, without installing anything into your real environment or assuming which installer (uv, pip, Poetry, Pipenv, ...) your project uses. A real example, checking requests against a Permissive-only policy:

$ trustedlicenses check requests
Resolving requests and its transitive dependencies...
Checking 5 package(s) (requested plus transitive dependencies)...
✗ Disallowed or undetectable licenses in 1 of 5 packages:
  certifi: detected MPL-2.0 (categories: Copyleft Limited) -- from declared metadata
    -> add "Copyleft Limited" to allowed-categories, or "certifi" to ignored-packages, to allow this

Accepting a non-standard license declaration

Some packages declare a license only as free text. catboost, for example, declares License: Apache License, Version 2.0 with no SPDX identifier, no classifier, and no bundled license file. trustedlicenses flags it, suggests Apache-2.0, and does not trust the suggestion by default. To accept it, pin the exact statement you reviewed:

[tool.trustedlicenses.verified-packages]
catboost = { statement = "Apache License, Version 2.0", spdx-id = "Apache-2.0" }

Unlike ignored-packages, which exempts a package by name whatever it declares later, a pin applies only while the declared statement still matches. If catboost later declares a different license, the pin stops applying and the package is checked against your policy again, so a strong-copyleft license fails unless your policy allows it. verified-statements does the same for one exact statement shared by several packages, and trust-corrected-licenses = true trusts every free-text correction project-wide. See the Usage Guide.

Why not just read pip list's license column?

Most Python license tools (pip-licenses, licensecheck) only read what a package says its license is, in its own metadata. That's usually right, but a meaningful slice of installed packages declare nothing usable at all — no metadata to read, so nothing to check.

trustedlicenses does that same check first, then — only when a package hasn't declared anything usable — actually reads the license text it ships and matches it against the official list of known open-source licenses. No extra software to install, and it doesn't need special system libraries the way some older tools in this space do. The matcher is the spdx Rust crate (word-bigram Sørensen–Dice matching against the SPDX license list), the same family of approach as GitHub's Licensee; trustedlicenses wraps it and does not reimplement it. A plain check makes no network calls at all; trustedlicenses index-check is a separate, opt-in command (see the Usage Guide) for the rarer case where nothing usable is declared or bundled, and the fix is upgrading.

See Comparison to Alternatives for how this differs from pip-licenses, licensecheck, liccheck, and ScanCode Toolkit in practice.

Speed

On a 425-package real-world environment (data science + web + cloud + ML stack, full transitive dependency trees — see Performance for exactly what's in it), trustedlicenses comes out fastest of the tools tested, despite doing genuine text-matching work for the ~23% of packages with no usable declared metadata:

Tool Median Range
trustedlicenses 1.29s 1.25s – 2.05s
pip-licenses 1.60s 1.56s – 2.58s
licensecheck 2.13s 2.08s – 9.30s
liccheck — crashes on a current Python/setuptools combination

That speed isn't free or automatic: without releasing Python's GIL during each Rust text-matching scan and without running scans in parallel, the same fallback work takes 7.7–8.9s on this same environment — roughly 6x slower. Instead, the fallback scans run concurrently across a thread pool, and the underlying Rust matcher releases the GIL for the duration of each scan, so independent per-package work actually runs on multiple cores rather than serializing behind Python's interpreter lock. See Performance for the full breakdown, methodology, and why liccheck couldn't be measured at all.

trustedlicenses is not a lawyer and does not provide legal advice. Its output — which license a package resolves to, which category that falls into, and whether a package passes your configured policy — is a best-effort technical signal, not a legal opinion. It can be wrong: a package's declared metadata can be inaccurate or absent, and the text-matching fallback is a similarity match with a real, disclosed false-negative/false-positive tradeoff (see Usage Guide § confidence threshold for a concrete case where this matters). Do not rely on trustedlicenses's output as a substitute for review by a qualified professional before making a legal or license- compliance decision. Use of this software is entirely at your own risk — see LICENSE for the full disclaimer of warranty.

See also: en.wikipedia.org/wiki/IANAL.

Status

Experimental — API and config format are not yet stable.

Metadata

Release files for trustedlicenses 0.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for trustedlicenses 0.3.2
File Size Uploaded Provenance Actions
trustedlicenses-0.3.2.tar.gz 94.5 kB With provenance Details for trustedlicenses-0.3.2.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for trustedlicenses 0.3.2
File Actions
trustedlicenses-0.3.2-cp311-abi3-win_amd64.whl CPython 3.11 abi3 Windows x86-64 Details for trustedlicenses-0.3.2-cp311-abi3-win_amd64.whl
trustedlicenses-0.3.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.11 abi3 Linux glibc 2.17+ x86-64 Details for trustedlicenses-0.3.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
trustedlicenses-0.3.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.11 abi3 Linux glibc 2.17+ ARM64 Details for trustedlicenses-0.3.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
trustedlicenses-0.3.2-cp311-abi3-macosx_11_0_arm64.whl CPython 3.11 abi3 macOS 11.0+ ARM64 Details for trustedlicenses-0.3.2-cp311-abi3-macosx_11_0_arm64.whl
trustedlicenses-0.3.2-cp311-abi3-macosx_10_12_x86_64.whl CPython 3.11 abi3 macOS 10.12+ x86-64 Details for trustedlicenses-0.3.2-cp311-abi3-macosx_10_12_x86_64.whl

Total release size: 15.2 MB

Release files / trustedlicenses-0.3.2.tar.gz

Download URL trustedlicenses-0.3.2.tar.gz
Size 94.5 kB
Tags Source
SHA-256 checksum
How to use checksums
8b2fd974ba152d4dcd42169443229d1671be5496059b4b76240670cbed4d3f31
BLAKE2b-256 checksum
How to use checksums
3ebdb46b14d6b9b9e06f3939436c7347f900ea2fd09a246f9315fea4a1a8134b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / trustedlicenses-0.3.2-cp311-abi3-win_amd64.whl

Download URL trustedlicenses-0.3.2-cp311-abi3-win_amd64.whl
Size 2.9 MB
Tags CPython 3.11 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
6e3e1dff61149fadfb1fbdfe58d524cdaef95f0d9d4592b90ed8943a80beadaf
BLAKE2b-256 checksum
How to use checksums
d476d217830472cd9d87ebbe9395615cd35cdd4516bb0dc3a4d9643897514375
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / trustedlicenses-0.3.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL trustedlicenses-0.3.2-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 3.1 MB
Tags CPython 3.11 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
b792bad2b34fb74be57dbdffee3eba15cf88e7330a7ae8f01b61d26488f56b4f
BLAKE2b-256 checksum
How to use checksums
2bb61dd801ea61809d4862e8f9aeeee69df87829ba431b369de96cc68e20dcac
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / trustedlicenses-0.3.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL trustedlicenses-0.3.2-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 3.0 MB
Tags CPython 3.11 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
90c56c97583cccab35734a6b8ea22ade1ca098a3f41546f35baebbddab0451a2
BLAKE2b-256 checksum
How to use checksums
3846730c676086ac04a57658d8b030c7d5361fdfd0a9a258e386243fc7c4834f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / trustedlicenses-0.3.2-cp311-abi3-macosx_11_0_arm64.whl

Download URL trustedlicenses-0.3.2-cp311-abi3-macosx_11_0_arm64.whl
Size 3.0 MB
Tags CPython 3.11 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
95508249b42a4ed7eed49c02b0292417fec4f8f5ff9e14cca0e71fe722cac62c
BLAKE2b-256 checksum
How to use checksums
964306d74a4f645d8a16b022fa22723e2a2c5dfa1750e207ef8ad822710a5313
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / trustedlicenses-0.3.2-cp311-abi3-macosx_10_12_x86_64.whl

Download URL trustedlicenses-0.3.2-cp311-abi3-macosx_10_12_x86_64.whl
Size 3.1 MB
Tags CPython 3.11 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
f15888c7664fb6d747b7b9d39d46c522de0aa3744cc58dcb13dcb8bf8560321f
BLAKE2b-256 checksum
How to use checksums
c1e87a221d9edd39ef52a6bb4b8b91046fdd7120f2da325729cbf813cce3a4db
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Security for trustedlicenses 0.3.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 6 files in this release have been uploaded with provenance metadata.

Select from 6 files

Attestation for trustedlicenses-0.3.2.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page