Skip to content

Commit f8d1a42

Browse files
committed
Store license-files in licenses subfolder
1 parent fb7f3d3 commit f8d1a42

File tree

6 files changed

+78
-9
lines changed

6 files changed

+78
-9
lines changed

newsfragments/4728.feature.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Store ``License-File``s in ``.dist-info/licenses`` subfolder and added support for recursive globs for ``license_files`` (`PEP 639 <https://peps.python.org/pep-0639/#add-license-expression-field>`_). -- by :user:`cdce8p`

setuptools/command/bdist_wheel.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -590,9 +590,11 @@ def adios(p: str) -> None:
590590
metadata_path = os.path.join(distinfo_path, "METADATA")
591591
shutil.copy(pkginfo_path, metadata_path)
592592

593+
licenses_folder_path = os.path.join(distinfo_path, "licenses")
593594
for license_path in self.license_paths:
594-
filename = os.path.basename(license_path)
595-
shutil.copy(license_path, os.path.join(distinfo_path, filename))
595+
dist_info_license_path = os.path.join(licenses_folder_path, license_path)
596+
os.makedirs(os.path.dirname(dist_info_license_path), exist_ok=True)
597+
shutil.copy(license_path, dist_info_license_path)
596598

597599
adios(egginfo_path)
598600

setuptools/dist.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,7 +418,10 @@ def _finalize_license_files(self) -> None:
418418
patterns = ['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']
419419

420420
self.metadata.license_files = list(
421-
unique_everseen(self._expand_patterns(patterns))
421+
map(
422+
lambda path: path.replace("\\", "/"),
423+
unique_everseen(self._expand_patterns(patterns)),
424+
)
422425
)
423426

424427
@staticmethod
@@ -432,7 +435,7 @@ def _expand_patterns(patterns):
432435
return (
433436
path
434437
for pattern in patterns
435-
for path in sorted(iglob(pattern))
438+
for path in sorted(iglob(pattern, recursive=True))
436439
if not path.endswith('~') and os.path.isfile(path)
437440
)
438441

setuptools/tests/test_bdist_wheel.py

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,20 @@
172172
),
173173
"README.rst": "UTF-8 描述 説明",
174174
},
175+
"licenses-dist": {
176+
"setup.cfg": cleandoc(
177+
"""
178+
[metadata]
179+
name = licenses-dist
180+
version = 1.0
181+
license_files = **/LICENSE
182+
"""
183+
),
184+
"LICENSE": "",
185+
"src": {
186+
"vendor": {"LICENSE": ""},
187+
},
188+
},
175189
}
176190

177191

@@ -238,6 +252,11 @@ def dummy_dist(tmp_path_factory):
238252
return mkexample(tmp_path_factory, "dummy-dist")
239253

240254

255+
@pytest.fixture
256+
def licenses_dist(tmp_path_factory):
257+
return mkexample(tmp_path_factory, "licenses-dist")
258+
259+
241260
def test_no_scripts(wheel_paths):
242261
"""Make sure entry point scripts are not generated."""
243262
path = next(path for path in wheel_paths if "complex_dist" in path)
@@ -297,7 +316,8 @@ def test_licenses_default(dummy_dist, monkeypatch, tmp_path):
297316
bdist_wheel_cmd(bdist_dir=str(tmp_path)).run()
298317
with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf:
299318
license_files = {
300-
"dummy_dist-1.0.dist-info/" + fname for fname in DEFAULT_LICENSE_FILES
319+
"dummy_dist-1.0.dist-info/licenses/" + fname
320+
for fname in DEFAULT_LICENSE_FILES
301321
}
302322
assert set(wf.namelist()) == DEFAULT_FILES | license_files
303323

@@ -311,7 +331,7 @@ def test_licenses_deprecated(dummy_dist, monkeypatch, tmp_path):
311331
bdist_wheel_cmd(bdist_dir=str(tmp_path)).run()
312332

313333
with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf:
314-
license_files = {"dummy_dist-1.0.dist-info/DUMMYFILE"}
334+
license_files = {"dummy_dist-1.0.dist-info/licenses/licenses/DUMMYFILE"}
315335
assert set(wf.namelist()) == DEFAULT_FILES | license_files
316336

317337

@@ -334,9 +354,29 @@ def test_licenses_override(dummy_dist, monkeypatch, tmp_path, config_file, confi
334354
bdist_wheel_cmd(bdist_dir=str(tmp_path)).run()
335355
with ZipFile("dist/dummy_dist-1.0-py3-none-any.whl") as wf:
336356
license_files = {
337-
"dummy_dist-1.0.dist-info/" + fname for fname in {"DUMMYFILE", "LICENSE"}
357+
"dummy_dist-1.0.dist-info/licenses/" + fname
358+
for fname in {"licenses/DUMMYFILE", "LICENSE"}
338359
}
339360
assert set(wf.namelist()) == DEFAULT_FILES | license_files
361+
metadata = wf.read("dummy_dist-1.0.dist-info/METADATA").decode("utf8")
362+
assert "License-File: licenses/DUMMYFILE" in metadata
363+
assert "License-File: LICENSE" in metadata
364+
365+
366+
def test_licenses_preserve_folder_structure(licenses_dist, monkeypatch, tmp_path):
367+
monkeypatch.chdir(licenses_dist)
368+
bdist_wheel_cmd(bdist_dir=str(tmp_path)).run()
369+
print(os.listdir("dist"))
370+
with ZipFile("dist/licenses_dist-1.0-py3-none-any.whl") as wf:
371+
default_files = {name.replace("dummy_", "licenses_") for name in DEFAULT_FILES}
372+
license_files = {
373+
"licenses_dist-1.0.dist-info/licenses/LICENSE",
374+
"licenses_dist-1.0.dist-info/licenses/src/vendor/LICENSE",
375+
}
376+
assert set(wf.namelist()) == default_files | license_files
377+
metadata = wf.read("licenses_dist-1.0.dist-info/METADATA").decode("utf8")
378+
assert "License-File: src/vendor/LICENSE" in metadata
379+
assert "License-File: LICENSE" in metadata
340380

341381

342382
def test_licenses_disabled(dummy_dist, monkeypatch, tmp_path):

setuptools/tests/test_build_meta.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -393,7 +393,9 @@ def test_build_with_pyproject_config(self, tmpdir, setup_script):
393393
with ZipFile(os.path.join(tmpdir, "temp", wheel_file)) as zipfile:
394394
wheel_contents = set(zipfile.namelist())
395395
metadata = str(zipfile.read("foo-0.1.dist-info/METADATA"), "utf-8")
396-
license = str(zipfile.read("foo-0.1.dist-info/LICENSE.txt"), "utf-8")
396+
license = str(
397+
zipfile.read("foo-0.1.dist-info/licenses/LICENSE.txt"), "utf-8"
398+
)
397399
epoints = str(zipfile.read("foo-0.1.dist-info/entry_points.txt"), "utf-8")
398400

399401
assert sdist_contents - {"foo-0.1/setup.py"} == {
@@ -426,7 +428,7 @@ def test_build_with_pyproject_config(self, tmpdir, setup_script):
426428
"foo/cli.py",
427429
"foo/data.txt", # include_package_data defaults to True
428430
"foo/py.typed", # include type information by default
429-
"foo-0.1.dist-info/LICENSE.txt",
431+
"foo-0.1.dist-info/licenses/LICENSE.txt",
430432
"foo-0.1.dist-info/METADATA",
431433
"foo-0.1.dist-info/WHEEL",
432434
"foo-0.1.dist-info/entry_points.txt",
@@ -438,6 +440,7 @@ def test_build_with_pyproject_config(self, tmpdir, setup_script):
438440
for line in (
439441
"Summary: This is a Python package",
440442
"License: MIT",
443+
"License-File: LICENSE.txt",
441444
"Classifier: Intended Audience :: Developers",
442445
"Requires-Dist: appdirs",
443446
"Requires-Dist: " + str(Requirement('tomli>=1 ; extra == "all"')),

setuptools/tests/test_egg_info.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,22 @@ def test_setup_cfg_license_file(self, tmpdir_cwd, env, files, license_in_sources
815815
[],
816816
id="files_only_added_once",
817817
),
818+
pytest.param(
819+
{
820+
'setup.cfg': DALS(
821+
"""
822+
[metadata]
823+
license_files = **/LICENSE
824+
"""
825+
),
826+
'LICENSE': "ABC license",
827+
'LICENSE-OTHER': "Don't include",
828+
'vendor': {'LICENSE': "Vendor license"},
829+
},
830+
['LICENSE', 'vendor/LICENSE'],
831+
['LICENSE-OTHER'],
832+
id="recursive_glob",
833+
),
818834
],
819835
)
820836
def test_setup_cfg_license_files(
@@ -1032,12 +1048,14 @@ def test_license_file_attr_pkg_info(self, tmpdir_cwd, env):
10321048
license_files =
10331049
NOTICE*
10341050
LICENSE*
1051+
**/LICENSE
10351052
"""
10361053
),
10371054
"LICENSE-ABC": "ABC license",
10381055
"LICENSE-XYZ": "XYZ license",
10391056
"NOTICE": "included",
10401057
"IGNORE": "not include",
1058+
"vendor": {'LICENSE': "Vendor license"},
10411059
})
10421060

10431061
environment.run_setup_py(
@@ -1053,9 +1071,11 @@ def test_license_file_attr_pkg_info(self, tmpdir_cwd, env):
10531071

10541072
# Only 'NOTICE', LICENSE-ABC', and 'LICENSE-XYZ' should have been matched
10551073
# Also assert that order from license_files is keeped
1074+
assert len(license_file_lines) == 4
10561075
assert "License-File: NOTICE" == license_file_lines[0]
10571076
assert "License-File: LICENSE-ABC" in license_file_lines[1:]
10581077
assert "License-File: LICENSE-XYZ" in license_file_lines[1:]
1078+
assert "License-File: vendor/LICENSE" in license_file_lines[3]
10591079

10601080
def test_metadata_version(self, tmpdir_cwd, env):
10611081
"""Make sure latest metadata version is used by default."""

0 commit comments

Comments
 (0)