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

Add more robust license file installation #621

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions flit_core/flit_core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@

log = logging.getLogger(__name__)

# These are the same patterns used by setuptools/wheel
# besides `LICENSES/*.txt` which is from the REUSE specification
LICENSE_PATTERNS = ('AUTHORS*', 'COPYING*', 'LICEN[CS]E*', 'LICENSES/*.txt')

class ConfigError(ValueError):
pass
Expand Down Expand Up @@ -257,6 +260,7 @@ def __init__(self):
self.sdist_exclude_patterns = []
self.dynamic_metadata = []
self.data_directory = None
self.license_files = []

def add_scripts(self, scripts_dict):
if scripts_dict:
Expand Down Expand Up @@ -401,6 +405,8 @@ def _prep_metadata(md_sect, path):
# For internal use, record the main requirements as a '.none' extra.
res.reqs_by_extra['.none'] = reqs_noextra

res.license_files = find_licenses(path.parent if path else Path("."))

return res

def _expand_requires_extra(re):
Expand Down Expand Up @@ -506,7 +512,7 @@ def read_pep621_metadata(proj, path) -> LoadedConfig:
"Unrecognised keys in [project.license]: {}".format(unrec_keys)
)

# TODO: Do something with license info.
# TODO: Include license info in the metadata.
# The 'License' field in packaging metadata is a brief description of
# a license, not the full text or a file path. PEP 639 will improve on
# how licenses are recorded.
Expand All @@ -515,13 +521,15 @@ def read_pep621_metadata(proj, path) -> LoadedConfig:
raise ConfigError(
"[project.license] should specify file or text, not both"
)
lc.referenced_files.append(license_tbl['file'])
lc.license_files.append(Path(license_tbl['file']))
elif 'text' in license_tbl:
pass
else:
raise ConfigError(
"file or text field required in [project.license] table"
)
if not lc.license_files:
lc.license_files = find_licenses(path.parent)

if 'authors' in proj:
_check_type(proj, 'authors', list)
Expand Down Expand Up @@ -658,3 +666,11 @@ def pep621_people(people, group_name='author') -> dict:
if emails:
res[group_name + '_email'] = ", ".join(emails)
return res

def find_licenses(path):
found = []
for pattern in LICENSE_PATTERNS:
found.extend(file for file in path.glob(pattern) if file.is_file())
if not found:
log.warning("No licenses were found in %s. Add a license!", LICENSE_PATTERNS)
return found
3 changes: 2 additions & 1 deletion flit_core/flit_core/sdist.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def from_ini_path(cls, ini_path: Path):
srcdir = ini_path.parent
module = common.Module(ini_info.module, srcdir)
metadata = common.make_metadata(module, ini_info)
extra_files = [ini_path.name] + ini_info.referenced_files
license_files = list(map(str, ini_info.license_files))
extra_files = [ini_path.name] + ini_info.referenced_files + license_files
return cls(
module, metadata, srcdir, ini_info.reqs_by_extra,
ini_info.entrypoints, extra_files, ini_info.data_directory,
Expand Down
17 changes: 17 additions & 0 deletions flit_core/flit_core/tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,20 @@ def test_bad_pep621_readme(readme, err_match):
}
with pytest.raises(config.ConfigError, match=err_match):
config.read_pep621_metadata(proj, samples_dir / 'pep621')

def test_license_file_auto_detect(tmp_path):
proj = {'name': 'module1', 'version': '1.0', 'description': 'x'}
tmp_path.joinpath('module1').mkdir()
for file in ('LICENSE', 'COPYING.md', 'module1/__init__.py'):
tmp_path.joinpath(file).touch()
parsed_config = config.read_pep621_metadata(proj, tmp_path / "pyproject.toml")
assert parsed_config.license_files == [tmp_path / 'COPYING.md', tmp_path / 'LICENSE']

def test_missing_license_warning(tmp_path, caplog):
proj = {'name': 'module1', 'version': '1.0', 'description': 'x'}
tmp_path.joinpath('module1').mkdir()
for file in ('module1/__init__.py',):
tmp_path.joinpath(file).touch()
config.read_pep621_metadata(proj, tmp_path / 'pyproject.toml')
message = f"No licenses were found in {config.LICENSE_PATTERNS}. Add a license!"
assert message in caplog.messages
13 changes: 7 additions & 6 deletions flit_core/flit_core/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ def zip_timestamp_from_env() -> Optional[tuple]:

class WheelBuilder:
def __init__(
self, directory, module, metadata, entrypoints, target_fp, data_directory
self, directory, module, metadata, entrypoints, target_fp, data_directory,
license_files
):
"""Build a wheel from a module/package
"""
Expand All @@ -69,6 +70,7 @@ def __init__(
self.metadata = metadata
self.entrypoints = entrypoints
self.data_directory = data_directory
self.license_files = license_files

self.records = []
self.source_time_stamp = zip_timestamp_from_env()
Expand All @@ -86,7 +88,8 @@ def from_ini_path(cls, ini_path, target_fp):
module = common.Module(ini_info.module, directory)
metadata = common.make_metadata(module, ini_info)
return cls(
directory, module, metadata, entrypoints, target_fp, ini_info.data_directory
directory, module, metadata, entrypoints, target_fp, ini_info.data_directory,
ini_info.license_files
)

@property
Expand Down Expand Up @@ -181,10 +184,8 @@ def write_metadata(self):
with self._write_to_zip(self.dist_info + '/entry_points.txt') as f:
common.write_entry_points(self.entrypoints, f)

for base in ('COPYING', 'LICENSE'):
for path in sorted(self.directory.glob(base + '*')):
if path.is_file():
self._add_file(path, '%s/%s' % (self.dist_info, path.name))
for path in self.license_files:
self._add_file(path, '%s/%s' % (self.dist_info, path.name))

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