Skip to content
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

License_files improvements #2620

Merged
merged 5 commits into from Apr 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions changelog.d/2620.breaking.rst
@@ -0,0 +1,4 @@
If neither ``license_file`` nor ``license_files`` is specified, the ``sdist``
option will now auto-include files that match the following patterns:
``LICEN[CS]E*``, ``COPYING*``, ``NOTICE*``, ``AUTHORS*``.
This matches the behavior of ``bdist_wheel``. -- by :user:`cdce8p`
1 change: 1 addition & 0 deletions changelog.d/2620.change.rst
@@ -0,0 +1 @@
The ``license_file`` and ``license_files`` options now support glob patterns. -- by :user:`cdce8p`
2 changes: 2 additions & 0 deletions changelog.d/2620.deprecation.rst
@@ -0,0 +1,2 @@
The ``license_file`` option is now marked as deprecated.
Use ``license_files`` instead. -- by :user:`cdce8p`
1 change: 1 addition & 0 deletions changelog.d/2620.doc.rst
@@ -0,0 +1 @@
Added documentation for the ``license_files`` option. -- by :user:`cdce8p`
11 changes: 11 additions & 0 deletions docs/references/keywords.rst
Expand Up @@ -76,6 +76,17 @@ Keywords
``license``
A string specifying the license of the package.

``license_file``

.. warning::
``license_file`` is deprecated. Use ``license_files`` instead.

``license_files``

A list of glob patterns for license related files that should be included.
If neither ``license_file`` nor ``license_files`` is specified, this option
defaults to ``LICEN[CS]E*``, ``COPYING*``, ``NOTICE*``, and ``AUTHORS*``.

``keywords``
A list of strings or a comma-separated string providing descriptive
meta-data. See: `PEP 0314`_.
Expand Down
2 changes: 1 addition & 1 deletion docs/userguide/declarative_config.rst
Expand Up @@ -184,7 +184,7 @@ maintainer_email maintainer-email str
classifiers classifier file:, list-comma
license str
license_file str
license_files list-comma
license_files list-comma 42.0.0
description summary file:, str
long_description long-description file:, str
long_description_content_type str 38.6.0
Expand Down
55 changes: 34 additions & 21 deletions setuptools/command/sdist.py
Expand Up @@ -4,6 +4,7 @@
import sys
import io
import contextlib
from glob import iglob

from setuptools.extern import ordered_set

Expand Down Expand Up @@ -194,29 +195,41 @@ def check_license(self):
"""Checks if license_file' or 'license_files' is configured and adds any
valid paths to 'self.filelist'.
"""

files = ordered_set.OrderedSet()

opts = self.distribution.get_option_dict('metadata')

# ignore the source of the value
_, license_file = opts.get('license_file', (None, None))

if license_file is None:
log.debug("'license_file' option was not specified")
else:
files.add(license_file)

files = ordered_set.OrderedSet()
try:
files.update(self.distribution.metadata.license_files)
license_files = self.distribution.metadata.license_files
except TypeError:
log.warn("warning: 'license_files' option is malformed")

for f in files:
if not os.path.exists(f):
log.warn(
"warning: Failed to find the configured license file '%s'",
f)
files.remove(f)

self.filelist.extend(files)
license_files = ordered_set.OrderedSet()
patterns = license_files if isinstance(license_files, ordered_set.OrderedSet) \
else ordered_set.OrderedSet(license_files)

if 'license_file' in opts:
log.warn(
"warning: the 'license_file' option is deprecated, "
"use 'license_files' instead")
patterns.append(opts['license_file'][1])

if 'license_file' not in opts and 'license_files' not in opts:
# Default patterns match the ones wheel uses
# See https://wheel.readthedocs.io/en/stable/user_guide.html
# -> 'Including license files in the generated wheel file'
patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*')
cdce8p marked this conversation as resolved.
Show resolved Hide resolved

for pattern in patterns:
for path in iglob(pattern):
if path.endswith('~'):
log.debug(
"ignoring license file '%s' as it looks like a backup",
path)
continue

if path not in files and os.path.isfile(path):
log.info(
"adding license file '%s' (matched pattern '%s')",
path, pattern)
files.add(path)

self.filelist.extend(sorted(files))
68 changes: 63 additions & 5 deletions setuptools/tests/test_egg_info.py
Expand Up @@ -537,15 +537,23 @@ def test_doesnt_provides_extra(self, tmpdir_cwd, env):
'setup.cfg': DALS("""
"""),
'LICENSE': "Test license"
}, False), # no license_file attribute
}, True), # no license_file attribute, LICENSE auto-included
({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE
"""),
'MANIFEST.in': "exclude LICENSE",
'LICENSE': "Test license"
}, False) # license file is manually excluded
}, False), # license file is manually excluded
pytest.param({
'setup.cfg': DALS("""
[metadata]
license_file = LICEN[CS]E*
"""),
'LICENSE': "Test license",
}, True,
id="glob_pattern"),
])
def test_setup_cfg_license_file(
self, tmpdir_cwd, env, files, license_in_sources):
Expand Down Expand Up @@ -625,7 +633,7 @@ def test_setup_cfg_license_file(
'setup.cfg': DALS("""
"""),
'LICENSE': "Test license"
}, [], ['LICENSE']), # no license_files attribute
}, ['LICENSE'], []), # no license_files attribute, LICENSE auto-included
({
'setup.cfg': DALS("""
[metadata]
Expand All @@ -644,7 +652,36 @@ def test_setup_cfg_license_file(
'MANIFEST.in': "exclude LICENSE-XYZ",
'LICENSE-ABC': "ABC license",
'LICENSE-XYZ': "XYZ license"
}, ['LICENSE-ABC'], ['LICENSE-XYZ']) # subset is manually excluded
}, ['LICENSE-ABC'], ['LICENSE-XYZ']), # subset is manually excluded
pytest.param({
'setup.cfg': "",
'LICENSE-ABC': "ABC license",
'COPYING-ABC': "ABC copying",
'NOTICE-ABC': "ABC notice",
'AUTHORS-ABC': "ABC authors",
'LICENCE-XYZ': "XYZ license",
'LICENSE': "License",
'INVALID-LICENSE': "Invalid license",
}, [
'LICENSE-ABC',
'COPYING-ABC',
'NOTICE-ABC',
'AUTHORS-ABC',
'LICENCE-XYZ',
'LICENSE',
], ['INVALID-LICENSE'],
# ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*')
id="default_glob_patterns"),
pytest.param({
'setup.cfg': DALS("""
[metadata]
license_files =
LICENSE*
"""),
'LICENSE-ABC': "ABC license",
'NOTICE-XYZ': "XYZ notice",
}, ['LICENSE-ABC'], ['NOTICE-XYZ'],
id="no_default_glob_patterns"),
])
def test_setup_cfg_license_files(
self, tmpdir_cwd, env, files, incl_licenses, excl_licenses):
Expand Down Expand Up @@ -749,7 +786,28 @@ def test_setup_cfg_license_files(
'LICENSE-PQR': "PQR license",
'LICENSE-XYZ': "XYZ license"
# manually excluded
}, ['LICENSE-XYZ'], ['LICENSE-ABC', 'LICENSE-PQR'])
}, ['LICENSE-XYZ'], ['LICENSE-ABC', 'LICENSE-PQR']),
pytest.param({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE*
"""),
'LICENSE-ABC': "ABC license",
'NOTICE-XYZ': "XYZ notice",
}, ['LICENSE-ABC'], ['NOTICE-XYZ'],
id="no_default_glob_patterns"),
pytest.param({
'setup.cfg': DALS("""
[metadata]
license_file = LICENSE*
license_files =
NOTICE*
"""),
'LICENSE-ABC': "ABC license",
'NOTICE-ABC': "ABC notice",
'AUTHORS-ABC': "ABC authors",
}, ['LICENSE-ABC', 'NOTICE-ABC'], ['AUTHORS-ABC'],
id="combined_glob_patterrns"),
])
def test_setup_cfg_license_file_license_files(
self, tmpdir_cwd, env, files, incl_licenses, excl_licenses):
Expand Down
1 change: 1 addition & 0 deletions setuptools/tests/test_manifest.py
Expand Up @@ -55,6 +55,7 @@ def touch(filename):
default_files = frozenset(map(make_local_path, [
'README.rst',
'MANIFEST.in',
'LICENSE',
'setup.py',
'app.egg-info/PKG-INFO',
'app.egg-info/SOURCES.txt',
Expand Down