case studies

Case Study: Typer Copied Code From Click, Then Shipped the Click Licence

Typer is a popular Python library for building command line tools. It began as a layer on top of Click, the command line library from the Pallets project, and for years it listed Click as a dependency. Typer is under the MIT licence. Click is under BSD-3-Clause, which lets anyone reuse its code as long as copies of the source keep Click’s copyright notice and licence text.

We ran Remixware 0.2.1 on two typer releases, each compared with the Click release of its day: typer 0.12.5 with Click 8.1.7, and the current typer 0.27.3 with Click 8.3.1. The full reports are in the Remixware repository, and every number here comes from them.

Typer 0.12.5 held 5 copies of Click code with no Click notice in its files. Typer 0.27.3 holds 61, all credited by typer/_click/LICENSE.txt.

Typer 0.12.5: Five Copies, No Click Notice

Remixware found five passages of Click code in typer 0.12.5, 99 lines in all. The smallest is a seven-line helper that splits an option such as --name into its prefix and its name, taken as is from Click’s parser. The largest is the method that writes an option’s line of help text. A comment in typer’s copy explains why: “Duplicate all of Click’s logic only to modify a single line”.

ATTRIBUTION MISSING  typer/core.py:49-55
                     click 8.1.7, src/click/parser.py:109-115 (BSD-3-Clause), copied as is, 100% match
                     Keep its copyright line and the BSD-3-Clause licence text with your copy, for
                     example in a third-party notices file.

No file in typer 0.12.5 had Click’s copyright line, so all five came out as attribution-missing. Click’s licence did reach users, inside the click package installed alongside typer. Typer’s own source didn’t carry it, and a copy of the source is what BSD-3-Clause’s first condition is about. A short notices file with Click’s copyright line and licence would have covered all five.

Typer 0.27.3: Click Moved In, With Its Licence

Typer no longer depends on Click. It carries the parts of Click it uses in a folder of its own, typer/_click, and the folder’s __init__.py says the code was taken and adapted from Click 8.3.1. Next to that code sits Click’s licence, typer/_click/LICENSE.txt, which starts with “Copyright 2014 Pallets”.

Compared with Click 8.3.1, typer 0.27.3 holds 61 passages of Click code, 5,366 lines. 39 are inside typer/_click and 22 are elsewhere in typer, among them the same option helper 0.12.5 had. Remixware reports all 61 as attributed:

ATTRIBUTED           typer/_click/core.py:58-419
                     click 8.3.1, src/click/core.py:100-686 (BSD-3-Clause), copied with edits, 88% match
                     Credited in typer/_click/LICENSE.txt, kept with the copy. Keep its copyright
                     line and the BSD-3-Clause licence text with your copy, for example in a
                     third-party notices file.

ATTRIBUTED           typer/core.py:36-42
                     click 8.3.1, src/click/parser.py:111-117 (BSD-3-Clause), copied as is, 100% match
                     Credited in typer/_click/LICENSE.txt. Keep its copyright line and the
                     BSD-3-Clause licence text with your copy, for example in a third-party notices
                     file.

What Typer Changed in Remixware

Remixware 0.2.0 looked for credit only in notice files at a project’s top folder, such as THIRD_PARTY_NOTICES.md, and in the copied file’s own header. It reported typer 0.27.3’s copies as missing their attribution, though Click’s licence was right there. Most projects that vendor a library keep its licence just that way, so Remixware 0.2.1 reads licence files kept with the code.

A copy inside typer/_click is credited by the licence file in its own folder. A copy elsewhere in typer is credited by the same file, because it ships with the project too. A licence inside a dependency folder such as vendor/ or node_modules/ still doesn’t count for code copied out of it, since a package manager can update that folder or remove it at any time.

To run the same check on a project of your own, index the code you think was copied and check against it:

remixware index path/to/click --out click.rmx
remixware check . --index click.rmx

The reference lists everything Remixware counts as credit.