Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Include license files in built distribution #18298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Aug 29, 2020
Merged

Include license files in built distribution #18298

merged 8 commits into from
Aug 29, 2020

Conversation

johnthagen
Copy link
Contributor

@johnthagen johnthagen commented Aug 19, 2020

PR Summary

Closes #18296

  • Configures wheel to include LICENSE files
  • Validates CI has included LICENSE files when building wheels
  • Normalize LICENSE file names
    • Note this is especially important for what was previously named Solarized.txt as there is no way for a tool like pip-licenses to glob for LICENSE file named that way that doesn't start with LICENSE

Note: wheel >= 0.32.0 is needed for this option to be honored.

PR Checklist

  • Has Pytest style unit tests
  • Code is Flake 8 compliant
  • New features are documented, with examples if plot related
  • Documentation is sphinx and numpydoc compliant
  • Added an entry to doc/users/next_whats_new/ if major new feature (follow instructions in README.rst there)
  • Documented in doc/api/next_api_changes/* if API changed in a backward-incompatible way

@tacaswell tacaswell added this to the v3.3.2 milestone Aug 19, 2020
@tacaswell
Copy link
Member

Thanks @johnthagen !

@jklymak
Copy link
Member

jklymak commented Aug 19, 2020

I'm no expert on setup tools, so I guess the question is if this is canonical or if pip-license should have been looking in the subdirectories?

@johnthagen
Copy link
Contributor Author

@jklymak This brings up a good point, in the source dist (.tar.gz file), the LICENSE folder/files are already included because they have been added to MANIFEST.in.

But they are not included in any of the wheel distributions. pip-licenses looks at the current virtual environment to find the LICENSE files, so if they are not included in the wheel there is no way for pip-licenses to find them.

The subfolder is an important note though. I'll check if pip-licenses in its current form can handle a folder of LICENSEs if they are included.

@jklymak
Copy link
Member

jklymak commented Aug 19, 2020

Yeah it was a comment from complete ignorance. It just seems we can't be the only project that has a sub directory? Not at all objecting to this PR though.

@johnthagen
Copy link
Contributor Author

From what I can tell, after matplotlib is installed, currently the LICENSEs don't end up in site-packages/matplotlib-3.3.1-py37.egg where they end up for other packages.

I think that setuptools/wheel have some default smarts to pull LICENSE* files into there when they are in the top directory. Since these are nested inside a folder, they don't get dropped in there by default. This PR should fix that.

I'm going to try to test the branch out with the sdist.

@jklymak jklymak requested a review from QuLogic August 19, 2020 14:56
@johnthagen
Copy link
Contributor Author

Well, it looks like perhaps license_files doesn't affect source dists: https://stackoverflow.com/questions/9977889/how-to-include-license-file-in-setup-py-script#comment104408651_48691876

So while the LICENSE files are included in the .tar.gz file, they aren't installed into site-packages/matplotlib-3.3.1-py37.dist-info (because there must be some magic in setuptools that looks for LICENSE* in the root folder).

I think this should work for wheels though, which is the default install most users will use.

Another option is to put multiple LICENSE files in the root of the project. opencv-python does this and they append all of the third party licenses together into a single file: https://github.com/skvark/opencv-python

@tacaswell
Copy link
Member

I am 👎 on moving the license files around or merging them into one file. There are already a bunch of tools out there that expect them to be where they are. If setuptools / wheel can not cope with this we should push back on that being an upstream bug.

@johnthagen johnthagen marked this pull request as draft August 19, 2020 23:50
@johnthagen
Copy link
Contributor Author

johnthagen commented Aug 19, 2020

So after some further investigation, I've now realized that this will need to be fixed via setup.cfg:

[metadata]
license_files = LICENSE/*

This was the only way that actually got the LICENSE files into the wheel. setuptools and wheel seem to have a lot of automatic support for looking for LICENSE files in the root of the project, which had skewed my understanding when solving this in the past.

The important reading:

I moved this PR to a Draft since I don't want to merge this as is as it doesn't actually solve the problem (sorry for not realizing this earlier).

matplotlib has a setup.cfg.template, is this used for the official builds pushed to PyPI? i.e. if we add the following lines to the template will that get used in the build?

I tested locally on macOS and with the setup.cfg changes in place in a file named setup.cfg, I can confirm the licenses get pulled into the wheel correctly.

@anntzer
Copy link
Contributor

anntzer commented Aug 20, 2020

In my view the fact that matplotlib reuses setup.cfg for its own purposes, even though it's really here for setuptools (distutils), is a minor but real annoyance (see e.g. the hacky del sdist.sdist.make_release_tree at the top of setup.py!) so I would be in favor of moving it to a separate mplsetup.cfg (or even mplsetupcfg.py, hehe).

@johnthagen johnthagen marked this pull request as ready for review August 20, 2020 10:48
@tacaswell
Copy link
Member

Can you also add a line to https://github.com/matplotlib/matplotlib/blob/master/.github/workflows/cibuildwheel.yml to copy the template over when we build the wheels?

@johnthagen
Copy link
Contributor Author

Can you also add a line to https://github.com/matplotlib/matplotlib/blob/master/.github/workflows/cibuildwheel.yml to copy the template over when we build the wheels?

@tacaswell Done. First time working with GitHub actions though. Is there a way to look at the built wheels for this PR so I can validate the LICENSE files made it in?

@tacaswell
Copy link
Member

I don't think we build wheels on every PR, but it is probably worth adding a job to travis or azure to build the wheel and check if the licences are in it to make sure we don't break this in the future. You can also add a check after we run the ciwheel build in the GH actions to not publish wheels without the license.

@QuLogic
Copy link
Member

QuLogic commented Aug 25, 2020

Wheels are currently built on master, v\d+.\d+.x and tags. They are not restricted by fork though, so you can simulate it by pushing to your own master branch. Just make sure to reset it afterwards, or you'll have the extra commits haunting you.

@johnthagen
Copy link
Contributor Author

For reference, the LICENSE files will be in:

matplotlib-<VERSION>-<PLATFORM>.whl
  matplotlib-<VERSION>.dist-info
    LICENSE*
    ...

@johnthagen
Copy link
Contributor Author

The simplest way I've found to check if the LICENSE files are being added to the wheel is:

$ python setup.py bdist_wheel | grep 'adding license file "LICENSE/LICENSE"'

This will return success/0 if the LICENSE files were added and failure/1 if not. This method may be a little brittle though as it relies on the stdout of wheel.

@johnthagen
Copy link
Contributor Author

Here is a Python script that will check if the LICENSE files are in the .whl files in dist. Is there a preference on which solution is used and if is so, where the script should live, etc.?

#!/usr/bin/env python3

"""Check that all .whl files in the dist folder have the correct LICENSE files included."""

from pathlib import Path
import sys
import zipfile

EXIT_SUCCESS = 0
EXIT_FAILURE = 1

LICENSE_FILE_NAMES = [
    'LICENSE',
    'LICENSE_AMSFONTS',
    'LICENSE_BAKOMA',
    'LICENSE_CARLOGO.txt',
    'LICENSE_COLORBREWER',
    'LICENSE_QT4_EDITOR',
    'LICENSE_STIX',
    'LICENSE_YORICK',
]


def main() -> int:
    dist_dir = Path(__file__).parent / 'dist'
    for wheel in dist_dir.glob('*.whl'):
        with zipfile.ZipFile(wheel) as f:
            license_file_names = [Path(path).name for path in sorted(f.namelist())
                                  if '.dist-info/LICENSE' in path]
            if license_file_names != LICENSE_FILE_NAMES:
                return EXIT_FAILURE
    return EXIT_SUCCESS


if __name__ == '__main__':
    sys.exit(main())

@QuLogic
Copy link
Member

QuLogic commented Aug 27, 2020

That script could be put in ci/; the LICENSE_FILE_NAMES could be generated from the same glob, and I'm not sure there's any need to have a main function.

@johnthagen
Copy link
Contributor Author

@QuLogic I have updated the files as requested. GitHub Actions now has a step that validates that LICENSE files has been included in all built wheels.

Copy link
Member

@tacaswell tacaswell left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this looks great @johnthagen

Co-authored-by: Elliott Sales de Andrade <[email protected]>
@johnthagen
Copy link
Contributor Author

I applied @QuLogic's latest suggestion, good catch. I believe this PR is ready for final review/merge.

@dopplershift dopplershift merged commit c75fad8 into matplotlib:master Aug 29, 2020
meeseeksmachine pushed a commit to meeseeksmachine/matplotlib that referenced this pull request Aug 29, 2020
dopplershift added a commit that referenced this pull request Aug 30, 2020
…298-on-v3.3.x

Backport PR #18298 on branch v3.3.x (Include license files in built distribution)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

LICENSE file(s) not included in published PyPI package
6 participants