Audit your dependencies' licenses offline and flag copyleft (GPL/LGPL/AGPL/MPL) risk. Scans site-packages, no account, no network. Zero dependencies.
Project description
licsniff
Audit your dependencies' licenses, offline. Point it at a virtualenv and get
a clean table of every installed package, its license, and a risk tier —
permissive, weak-copyleft, strong-copyleft, proprietary, or unknown —
so a stray GPL transitive dep can't sneak into your closed-source product.
No account. No network. No config. Zero dependencies (pure standard library).
pipx run licsniff
PACKAGE VERSION LICENSE RISK
some-gpl-lib 2.1.0 GPL-3.0 strong-copyleft
mystery-pkg 0.0.3 (none) unknown
copyleft-utils 1.4.0 LGPL-2.1 weak-copyleft
requests 2.32.3 Apache-2.0 permissive
packaging 24.1 Apache-2.0 OR BSD-2-Clause permissive
Riskiest first, so the line you need to worry about is at the top.
Why another license tool?
The hosted options — Snyk, FOSSA, Black Duck — all want a signup, a token, and a
network round-trip before they'll tell you something your site-packages
already knows. The Node ecosystem's go-to,
license-checker, has been
unmaintained for years and doesn't read Python anyway.
licsniff is the gap: it reads the *.dist-info/METADATA files already on your
disk, classifies each license locally, and exits. Nothing leaves the machine.
It also does the part those tools are vague about — tiering by actual risk.
Knowing a dep is "GPL-3.0" only helps if you know GPL is strong copyleft and
MPL is weak. licsniff bakes that in, understands SPDX expressions
((MIT OR Apache-2.0) → least-restrictive, GPL-3.0 AND MIT →
most-restrictive), and normalizes the messy variants (GPLv3, GPL-3.0+,
GPL-3.0-only, Apache License 2.0).
Install
pipx run licsniff # no install, run on demand
pip install licsniff # or install the `licsniff` command
There's an identical Node build too: npx licsniff / npm i -g licsniff
(see licsniff) — it audits a Node project's
node_modules instead of site-packages. Both ports share the exact same
classifier, tested against the same vectors, so they tier licenses byte-for-byte
the same.
Usage
licsniff [options] # scans the active venv's site-packages
licsniff --path <dir> [options] # scan a specific site-packages folder
By default licsniff resolves your $VIRTUAL_ENV site-packages, falling back to
./.venv/lib/python*/site-packages. Pass --path to point it anywhere.
| Option | Description |
|---|---|
--path <dir> |
site-packages dir to scan. |
--summary |
Print counts per risk tier instead of the full table. |
--json |
Machine-readable JSON ({path, total, counts, packages[]}). |
--fail-on <tier> |
Exit 1 if any package is at or above <tier>. CI gate. |
--no-color |
Disable ANSI color. |
-h, --help |
Show help. |
-v, --version |
Print version. |
Risk tiers
| Tier | Examples | What it means |
|---|---|---|
permissive |
MIT, ISC, BSD-2/3-Clause, Apache-2.0, 0BSD, Unlicense, CC0 | Use freely, just keep the notice. |
weak-copyleft |
LGPL-*, MPL-2.0, EPL-*, CDDL-* | File-level / linking obligations. |
strong-copyleft |
GPL-*, AGPL-* | Can force you to open-source your code. |
proprietary |
UNLICENSED, "SEE LICENSE IN …" | Not open source — read the terms. |
unknown |
missing / unrecognized | No idea — investigate manually. |
Examples
# counts at a glance
licsniff --summary
# CI gate: fail the build if anything copyleft-or-worse slipped in
licsniff --fail-on strong-copyleft
# pipe to jq
licsniff --json | jq '.packages[] | select(.tier=="unknown") | .name'
# audit a specific environment
licsniff --path /path/to/venv/lib/python3.11/site-packages
Design notes
- One pure function at the core.
classify_license(id_or_name)→{tier, spdx}has no I/O, no clock, no globals. The CLI is a thinsite-packagesreader wrapped around it. That's what makes the Node and Python ports verifiably identical — they share one test table. - It reads the real METADATA fields. The modern PEP 639
License-Expression:(SPDX), the legacyLicense:field, and theLicense :: …trove classifiers — preferring the classifier when theLicense:field is empty,UNKNOWN, or a pasted-in wall of license text. - SPDX expressions are evaluated, not guessed.
ORpicks the least restrictive option (you get to choose),ANDpicks the most restrictive (you must satisfy all).WITHexception clauses fall back to the base license. - Fully offline, read-only. It never writes anything and never opens a socket. Safe to run anywhere, including air-gapped CI.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file licsniff-0.1.0.tar.gz.
File metadata
- Download URL: licsniff-0.1.0.tar.gz
- Upload date:
- Size: 16.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
653a12c378518099ade6a3146a1f847130fddc94b6aa9f299b541b6a7a37a17f
|
|
| MD5 |
e0f55e4f36adeddafcd8bdb42aafa833
|
|
| BLAKE2b-256 |
6b2c400034acba1c216f76c10d7953ecce08a49009c9c86a190dfafdf8225c80
|
File details
Details for the file licsniff-0.1.0-py3-none-any.whl.
File metadata
- Download URL: licsniff-0.1.0-py3-none-any.whl
- Upload date:
- Size: 14.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbf546b375c663ff3585b5d3fb097d435f9e9d8d8bdf8566807c860e93c4a44c
|
|
| MD5 |
b3f758943a4fe0bbd19f41b06d550298
|
|
| BLAKE2b-256 |
b2c8d0d68190ca30a0db390abe0f765069cafe7a31701474e9295135b30b4189
|