Skip to content

Commit f08bb9f

Browse files
authored
Merge branch 'main' into license-expression
2 parents e08f30f + c13d414 commit f08bb9f

File tree

15 files changed

+215
-14
lines changed

15 files changed

+215
-14
lines changed

.github/workflows/test.yml

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,33 @@ jobs:
2121
strategy:
2222
matrix:
2323
platform: ["ubuntu-latest", "windows-latest"]
24-
python-version: [ "3.7", "3.8", "3.9", "3.10", "3.11" , "3.12", "3.13" ]
24+
python-version: [ "3.8", "3.9", "3.10", "3.11" , "3.12", "3.13" ]
25+
steps:
26+
- uses: actions/checkout@v4
27+
28+
- name: Setup Python ${{ matrix.python-version }}
29+
uses: actions/setup-python@v5
30+
with:
31+
python-version: ${{ matrix.python-version }}
32+
33+
- name: Install dependencies
34+
run: |
35+
python -m pip install --upgrade pip
36+
pip install tox tox-gh-actions codecov
37+
38+
- name: Run tests
39+
run: tox
40+
41+
- name: Codecov upload
42+
env:
43+
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
44+
run: codecov
45+
46+
test-py37:
47+
runs-on: "ubuntu-22.04"
48+
strategy:
49+
matrix:
50+
python-version: [ "3.7", ]
2551
steps:
2652
- uses: actions/checkout@v4
2753

doc/pyproject_toml.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ license
9999
A valid SPDX `license expression <https://peps.python.org/pep-0639/#term-license-expression>`_
100100
or a table with either a ``file`` key (a relative path to a license file) or a
101101
``text`` key (the license text).
102+
license-files
103+
A list of glob patterns for license files to include.
104+
Defaults to ``['COPYING*', 'LICEN[CS]E*']``.
102105
authors
103106
A list of tables with ``name`` and ``email`` keys (both optional) describing
104107
the authors of the project.

flit_core/flit_core/common.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,7 @@ class Metadata(object):
355355
obsoletes_dist = ()
356356
requires_external = ()
357357
provides_extra = ()
358+
license_files = ()
358359
dynamic = ()
359360

360361
metadata_version = "2.3"
@@ -436,6 +437,10 @@ def write_metadata_file(self, fp):
436437
for clsfr in self.classifiers:
437438
fp.write(u'Classifier: {}\n'.format(clsfr))
438439

440+
# TODO: License-File requires Metadata-Version '2.4'
441+
# for file in self.license_files:
442+
# fp.write(u'License-File: {}\n'.format(file))
443+
439444
for req in self.requires_dist:
440445
normalised_req = self._normalise_requires_dist(req)
441446
fp.write(u'Requires-Dist: {}\n'.format(normalised_req))

flit_core/flit_core/config.py

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class ConfigError(ValueError):
6060
'readme',
6161
'requires-python',
6262
'license',
63+
'license-files',
6364
'authors',
6465
'maintainers',
6566
'keywords',
@@ -73,6 +74,9 @@ class ConfigError(ValueError):
7374
'dynamic',
7475
}
7576

77+
default_license_files_globs = ['COPYING*', 'LICEN[CS]E*']
78+
license_files_allowed_chars = re.compile(r'^[\w\-\.\/\*\?\[\]]+$')
79+
7680

7781
def read_flit_config(path):
7882
"""Read and check the `pyproject.toml` file with data about the package.
@@ -427,6 +431,15 @@ def _prep_metadata(md_sect, path):
427431
# For internal use, record the main requirements as a '.none' extra.
428432
res.reqs_by_extra['.none'] = reqs_noextra
429433

434+
if path:
435+
license_files = sorted(
436+
_license_files_from_globs(
437+
path.parent, default_license_files_globs, warn_no_files=False
438+
)
439+
)
440+
res.referenced_files.extend(license_files)
441+
md_dict['license_files'] = license_files
442+
430443
return res
431444

432445
def _expand_requires_extra(re):
@@ -439,6 +452,43 @@ def _expand_requires_extra(re):
439452
yield '{} ; extra == "{}"'.format(req, extra)
440453

441454

455+
def _license_files_from_globs(project_dir: Path, globs, warn_no_files = True):
456+
license_files = set()
457+
for pattern in globs:
458+
if isabs_ish(pattern):
459+
raise ConfigError(
460+
"Invalid glob pattern for [project.license-files]: '{}'. "
461+
"Pattern must not start with '/'.".format(pattern)
462+
)
463+
if ".." in pattern:
464+
raise ConfigError(
465+
"Invalid glob pattern for [project.license-files]: '{}'. "
466+
"Pattern must not contain '..'".format(pattern)
467+
)
468+
if license_files_allowed_chars.match(pattern) is None:
469+
raise ConfigError(
470+
"Invalid glob pattern for [project.license-files]: '{}'. "
471+
"Pattern contains invalid characters. "
472+
"https://packaging.python.org/en/latest/specifications/pyproject-toml/#license-files"
473+
)
474+
try:
475+
files = [
476+
str(file.relative_to(project_dir)).replace(osp.sep, "/")
477+
for file in project_dir.glob(pattern)
478+
if file.is_file()
479+
]
480+
except ValueError as ex:
481+
raise ConfigError(
482+
"Invalid glob pattern for [project.license-files]: '{}'. {}".format(pattern, ex.args[0])
483+
)
484+
485+
if not files and warn_no_files:
486+
raise ConfigError(
487+
"No files found for [project.license-files]: '{}' pattern".format(pattern)
488+
)
489+
license_files.update(files)
490+
return license_files
491+
442492
def _check_type(d, field_name, cls):
443493
if not isinstance(d[field_name], cls):
444494
raise ConfigError(
@@ -533,6 +583,7 @@ def read_pep621_metadata(proj, path) -> LoadedConfig:
533583
if 'requires-python' in proj:
534584
md_dict['requires_python'] = proj['requires-python']
535585

586+
license_files = set()
536587
if 'license' in proj:
537588
_check_types(proj, 'license', (str, dict))
538589
if isinstance(proj['license'], str):
@@ -554,14 +605,31 @@ def read_pep621_metadata(proj, path) -> LoadedConfig:
554605
)
555606
lc.referenced_files.append(license_tbl['file'])
556607
elif 'text' in license_tbl:
557-
license = license_tbl['text']
558-
# TODO Normalize license if it's a valid SPDX expression
559-
md_dict['license'] = license
608+
pass
560609
else:
561610
raise ConfigError(
562611
"file or text field required in [project.license] table"
563612
)
564613

614+
if 'license-files' in proj:
615+
_check_type(proj, 'license-files', list)
616+
globs = proj['license-files']
617+
license_files = _license_files_from_globs(path.parent, globs)
618+
if isinstance(proj.get('license'), dict):
619+
raise ConfigError(
620+
"license-files cannot be used with a license table, "
621+
"use 'project.license' with a license expression instead"
622+
)
623+
else:
624+
license_files.update(
625+
_license_files_from_globs(
626+
path.parent, default_license_files_globs, warn_no_files=False
627+
)
628+
)
629+
license_files_sorted = sorted(license_files)
630+
lc.referenced_files.extend(license_files_sorted)
631+
md_dict['license_files'] = license_files_sorted
632+
565633
if 'authors' in proj:
566634
_check_type(proj, 'authors', list)
567635
md_dict.update(pep621_people(proj['authors']))

flit_core/flit_core/wheel.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,8 @@ def write_metadata(self):
183183
with self._write_to_zip(self.dist_info + '/entry_points.txt') as f:
184184
common.write_entry_points(self.entrypoints, f)
185185

186-
for base in ('COPYING', 'LICENSE'):
187-
for path in sorted(self.directory.glob(base + '*')):
188-
if path.is_file():
189-
self._add_file(path, '%s/%s' % (self.dist_info, path.name))
186+
for file in self.metadata.license_files:
187+
self._add_file(self.directory / file, '%s/licenses/%s' % (self.dist_info, file))
190188

191189
with self._write_to_zip(self.dist_info + '/WHEEL') as f:
192190
_write_wheel_file(f, supports_py2=self.metadata.supports_py2)

flit_core/pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ dependencies = []
1313
requires-python = '>=3.6'
1414
readme = "README.rst"
1515
license = "BSD-3-Clause"
16+
license-files = ["LICENSE*", "flit_core/vendor/**/LICENSE*"]
1617
classifiers = [
1718
"Topic :: Software Development :: Libraries :: Python Modules",
1819
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This file should be added to wheels
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Readme
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This file should be added to wheels
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""Example module"""
2+
3+
__version__ = '0.1'

0 commit comments

Comments
 (0)