From badfe739c61dd6f3609b4e3854519cfbe663c5a2 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 30 Mar 2021 16:51:52 +0200 Subject: Write long description in message payload --- setuptools/dist.py | 8 ++++++-- setuptools/tests/test_egg_info.py | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index a1b7e832..71b31873 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -176,8 +176,9 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for project_url in self.project_urls.items(): write_field('Project-URL', '%s, %s' % project_url) - long_desc = rfc822_escape(self.get_long_description()) - write_field('Description', long_desc) + if version < StrictVersion('2.1'): + long_desc = rfc822_escape(self.get_long_description()) + write_field('Description', long_desc) keywords = ','.join(self.get_keywords()) if keywords: @@ -207,6 +208,9 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for extra in self.provides_extras: write_field('Provides-Extra', extra) + if version >= StrictVersion('2.1'): + file.write("\n%s\n\n" % self.get_long_description()) + sequence = tuple, list diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 0d595ad0..ba683d08 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -875,6 +875,27 @@ class TestEggInfo: assert expected_line in pkg_info_lines assert 'Metadata-Version: 2.1' in pkg_info_lines + def test_description(self, tmpdir_cwd, env): + self._setup_script_with_requires( + "long_description='This is a long description\\nover multiple lines',") + environ = os.environ.copy().update( + HOME=env.paths['home'], + ) + code, data = environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + env=environ, + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO')) as pkginfo_file: + pkg_info_lines = pkginfo_file.read().split('\n') + assert 'Metadata-Version: 2.1' in pkg_info_lines + assert '' == pkg_info_lines[-1] + long_desc_lines = pkg_info_lines[pkg_info_lines.index(''):] + assert 'This is a long description' in long_desc_lines + assert 'over multiple lines' in long_desc_lines + def test_project_urls(self, tmpdir_cwd, env): # Test that specifying a `project_urls` dict to the `setup` # function results in writing multiple `Project-URL` lines to -- cgit v1.2.1 From 56bd73b26d379f1423ae8816941018c6a48c0ef7 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Tue, 30 Mar 2021 19:11:21 +0200 Subject: Fix tests --- setuptools/dist.py | 20 ++++++++++++++++++++ setuptools/tests/test_egg_info.py | 10 ++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 71b31873..1eb51ba4 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -96,6 +96,24 @@ def read_pkg_file(self, file): """Reads the metadata values from a file object.""" msg = message_from_file(file) + def _read_long_description(): + value = msg['description'] + if value in ('UNKNOWN', None): + return None + description_lines = value.splitlines() + if len(description_lines) == 1: + return description_lines[0].lstrip() + description_dedent = '\n'.join( + (description_lines[0].lstrip(), + textwrap.dedent('\n'.join(description_lines[1:])))) + return description_dedent + + def _read_payload(): + value = msg.get_payload().strip() + if value == 'UNKNOWN': + return None + return value + self.metadata_version = StrictVersion(msg['metadata-version']) self.name = _read_field_from_msg(msg, 'name') self.version = _read_field_from_msg(msg, 'version') @@ -114,6 +132,8 @@ def read_pkg_file(self, file): self.download_url = None self.long_description = _read_field_unescaped_from_msg(msg, 'description') + if self.long_description is None and self.metadata_version >= StrictVersion('2.1'): + self.long_description = _read_payload() self.description = _read_field_from_msg(msg, 'summary') if 'keywords' in msg: diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index ba683d08..d0183eef 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -875,9 +875,15 @@ class TestEggInfo: assert expected_line in pkg_info_lines assert 'Metadata-Version: 2.1' in pkg_info_lines - def test_description(self, tmpdir_cwd, env): + def test_long_description(self, tmpdir_cwd, env): + # Test that specifying `long_description` and `long_description_content_type` + # keyword args to the `setup` function results in writing + # the description in the message payload of the `PKG-INFO` file + # in the `.egg-info` directory. self._setup_script_with_requires( - "long_description='This is a long description\\nover multiple lines',") + "long_description='This is a long description\\nover multiple lines'," + "long_description_content_type='text/markdown'," + ) environ = os.environ.copy().update( HOME=env.paths['home'], ) -- cgit v1.2.1 From d1a491ec8144ba856b74467b551eab359e86b659 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 15 Apr 2021 02:01:17 +0200 Subject: Changes after rebase --- setuptools/dist.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 1eb51ba4..961b3cbd 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -92,28 +92,17 @@ def _read_list_from_msg(msg: "Message", field: str) -> Optional[List[str]]: return values +def _read_payload_from_msg(msg: "Message") -> Optional[str]: + value = msg.get_payload().strip() + if value == 'UNKNOWN': + return None + return value + + def read_pkg_file(self, file): """Reads the metadata values from a file object.""" msg = message_from_file(file) - def _read_long_description(): - value = msg['description'] - if value in ('UNKNOWN', None): - return None - description_lines = value.splitlines() - if len(description_lines) == 1: - return description_lines[0].lstrip() - description_dedent = '\n'.join( - (description_lines[0].lstrip(), - textwrap.dedent('\n'.join(description_lines[1:])))) - return description_dedent - - def _read_payload(): - value = msg.get_payload().strip() - if value == 'UNKNOWN': - return None - return value - self.metadata_version = StrictVersion(msg['metadata-version']) self.name = _read_field_from_msg(msg, 'name') self.version = _read_field_from_msg(msg, 'version') @@ -133,7 +122,7 @@ def read_pkg_file(self, file): self.long_description = _read_field_unescaped_from_msg(msg, 'description') if self.long_description is None and self.metadata_version >= StrictVersion('2.1'): - self.long_description = _read_payload() + self.long_description = _read_payload_from_msg(msg) self.description = _read_field_from_msg(msg, 'summary') if 'keywords' in msg: -- cgit v1.2.1 From 1069ae4e3554a8828075a83dedc77e0e212637c7 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 15 Apr 2021 23:10:40 +0200 Subject: Small changes after review --- setuptools/tests/test_egg_info.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index d0183eef..22e970a4 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -884,21 +884,17 @@ class TestEggInfo: "long_description='This is a long description\\nover multiple lines'," "long_description_content_type='text/markdown'," ) - environ = os.environ.copy().update( - HOME=env.paths['home'], - ) code, data = environment.run_setup_py( cmd=['egg_info'], pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), data_stream=1, - env=environ, ) egg_info_dir = os.path.join('.', 'foo.egg-info') with open(os.path.join(egg_info_dir, 'PKG-INFO')) as pkginfo_file: pkg_info_lines = pkginfo_file.read().split('\n') assert 'Metadata-Version: 2.1' in pkg_info_lines assert '' == pkg_info_lines[-1] - long_desc_lines = pkg_info_lines[pkg_info_lines.index(''):] + long_desc_lines = pkg_info_lines[8:] assert 'This is a long description' in long_desc_lines assert 'over multiple lines' in long_desc_lines -- cgit v1.2.1 From 34c31ebe437edf9d6d4ee5010461668156793569 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 9 May 2021 20:16:37 +0200 Subject: Changes after rebase --- setuptools/dist.py | 7 +------ setuptools/tests/test_dist.py | 4 ++-- setuptools/tests/test_egg_info.py | 4 ++-- 3 files changed, 5 insertions(+), 10 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 961b3cbd..bf3b9461 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -185,10 +185,6 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for project_url in self.project_urls.items(): write_field('Project-URL', '%s, %s' % project_url) - if version < StrictVersion('2.1'): - long_desc = rfc822_escape(self.get_long_description()) - write_field('Description', long_desc) - keywords = ','.join(self.get_keywords()) if keywords: write_field('Keywords', keywords) @@ -217,8 +213,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for extra in self.provides_extras: write_field('Provides-Extra', extra) - if version >= StrictVersion('2.1'): - file.write("\n%s\n\n" % self.get_long_description()) + file.write("\n%s\n\n" % self.get_long_description()) sequence = tuple, list diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index 6378caef..c4279f0b 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -251,8 +251,8 @@ def test_maintainer_author(name, attrs, tmpdir): with io.open(str(fn.join('PKG-INFO')), 'r', encoding='utf-8') as f: raw_pkg_lines = f.readlines() - # Drop blank lines - pkg_lines = list(filter(None, raw_pkg_lines)) + # Drop blank lines and strip lines from default description + pkg_lines = list(filter(None, raw_pkg_lines[:-2])) pkg_lines_set = set(pkg_lines) diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 22e970a4..d7657a47 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -893,8 +893,8 @@ class TestEggInfo: with open(os.path.join(egg_info_dir, 'PKG-INFO')) as pkginfo_file: pkg_info_lines = pkginfo_file.read().split('\n') assert 'Metadata-Version: 2.1' in pkg_info_lines - assert '' == pkg_info_lines[-1] - long_desc_lines = pkg_info_lines[8:] + assert '' == pkg_info_lines[-1] # last line should be empty + long_desc_lines = pkg_info_lines[pkg_info_lines.index(''):] assert 'This is a long description' in long_desc_lines assert 'over multiple lines' in long_desc_lines -- cgit v1.2.1 From 3544de73b3662a27fac14d8eb9f5c841668d66de Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 17 Apr 2021 20:48:12 +0200 Subject: Add License-File field to package metadata --- setuptools/command/egg_info.py | 9 +++++++- setuptools/command/sdist.py | 46 --------------------------------------- setuptools/config.py | 5 +++++ setuptools/dist.py | 37 +++++++++++++++++++++++++++++-- setuptools/tests/test_egg_info.py | 29 ++++++++++++++++++++++++ setuptools/tests/test_manifest.py | 1 - 6 files changed, 77 insertions(+), 50 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 1f120b67..26ff9a4c 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -539,6 +539,7 @@ class manifest_maker(sdist): if not os.path.exists(self.manifest): self.write_manifest() # it must exist so it'll get in the list self.add_defaults() + self.add_license_files() if os.path.exists(self.template): self.read_template() self.prune_file_list() @@ -575,7 +576,6 @@ class manifest_maker(sdist): def add_defaults(self): sdist.add_defaults(self) - self.check_license() self.filelist.append(self.template) self.filelist.append(self.manifest) rcfiles = list(walk_revctrl()) @@ -592,6 +592,13 @@ class manifest_maker(sdist): ei_cmd = self.get_finalized_command('egg_info') self.filelist.graft(ei_cmd.egg_info) + def add_license_files(self): + license_files = self.distribution.metadata.license_files_computed + for lf in license_files: + log.info("adding license file '%s'", lf) + pass + self.filelist.extend(license_files) + def prune_file_list(self): build = self.get_finalized_command('build') base_dir = self.distribution.get_fullname() diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py index a6ea814a..4a014283 100644 --- a/setuptools/command/sdist.py +++ b/setuptools/command/sdist.py @@ -4,9 +4,6 @@ import os import sys import io import contextlib -from glob import iglob - -from setuptools.extern import ordered_set from .py36compat import sdist_add_defaults @@ -190,46 +187,3 @@ class sdist(sdist_add_defaults, orig.sdist): continue self.filelist.append(line) manifest.close() - - def check_license(self): - """Checks if license_file' or 'license_files' is configured and adds any - valid paths to 'self.filelist'. - """ - opts = self.distribution.get_option_dict('metadata') - - files = ordered_set.OrderedSet() - try: - license_files = self.distribution.metadata.license_files - except TypeError: - log.warn("warning: 'license_files' option is malformed") - 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*') - - 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)) diff --git a/setuptools/config.py b/setuptools/config.py index 4a6cd469..44de7cf5 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -520,6 +520,11 @@ class ConfigMetadataHandler(ConfigHandler): 'obsoletes': parse_list, 'classifiers': self._get_parser_compound(parse_file, parse_list), 'license': exclude_files_parser('license'), + 'license_file': self._deprecated_config_handler( + exclude_files_parser('license_file'), + "The license_file parameter is deprecated, " + "use license_files instead.", + DeprecationWarning), 'license_files': parse_list, 'description': parse_file, 'long_description': parse_file, diff --git a/setuptools/dist.py b/setuptools/dist.py index bf3b9461..9d7fb751 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -15,9 +15,10 @@ import distutils.command from distutils.util import strtobool from distutils.debug import DEBUG from distutils.fancy_getopt import translate_longopt +from glob import iglob import itertools import textwrap -from typing import List, Optional, TYPE_CHECKING +from typing import List, Optional, Set, TYPE_CHECKING from collections import defaultdict from email import message_from_file @@ -141,6 +142,8 @@ def read_pkg_file(self, file): self.provides = None self.obsoletes = None + self.license_files_computed = _read_list_from_msg(msg, 'license-file') + def single_line(val): # quick and dirty validation for description pypa/setuptools#1390 @@ -213,6 +216,8 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for extra in self.provides_extras: write_field('Provides-Extra', extra) + self._write_list(file, 'License-File', self.license_files_computed) + file.write("\n%s\n\n" % self.get_long_description()) @@ -414,7 +419,9 @@ class Distribution(_Distribution): 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, - 'license_files': ordered_set.OrderedSet, + 'license_file': None, + 'license_files': None, + 'license_files_computed': list, } _patched_dist = None @@ -573,6 +580,31 @@ class Distribution(_Distribution): req.marker = None return req + def _finalize_license_files(self): + """Compute names of all license files which should be included.""" + files = set() + license_files: Optional[List[str]] = self.metadata.license_files + patterns: Set[str] = set(license_files) if license_files else set() + + license_file: Optional[str] = self.metadata.license_file + if license_file: + patterns.add(license_file) + + if license_files is None and license_file is None: + # 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*') + + for pattern in patterns: + for path in iglob(pattern): + if path.endswith('~'): + continue + if path not in files and os.path.isfile(path): + files.add(path) + + self.metadata.license_files_computed = sorted(files) + # FIXME: 'Distribution._parse_config_files' is too complex (14) def _parse_config_files(self, filenames=None): # noqa: C901 """ @@ -737,6 +769,7 @@ class Distribution(_Distribution): parse_configuration(self, self.command_options, ignore_option_errors=ignore_option_errors) self._finalize_requires() + self._finalize_license_files() def fetch_build_eggs(self, requires): """Resolve pre-setup requirements""" diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index d7657a47..29fb2062 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -835,6 +835,35 @@ class TestEggInfo: for lf in excl_licenses: assert sources_lines.count(lf) == 0 + def test_license_file_attr_pkg_info(self, tmpdir_cwd, env): + """All matched license files should have a corresponding License-File.""" + self._create_project() + path.build({ + "setup.cfg": DALS(""" + [metadata] + license_files = + LICENSE* + """), + "LICENSE-ABC": "ABC license", + "LICENSE-XYZ": "XYZ license", + "NOTICE": "not included", + }) + + environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]) + ) + egg_info_dir = os.path.join('.', 'foo.egg-info') + with open(os.path.join(egg_info_dir, 'PKG-INFO')) as pkginfo_file: + pkg_info_lines = pkginfo_file.read().split('\n') + license_file_lines = [ + line for line in pkg_info_lines if line.startswith('License-File:')] + + # Only 'LICENSE-ABC' and 'LICENSE-XYZ' should have been matched + assert len(license_file_lines) == 2 + assert "License-File: LICENSE-ABC" in license_file_lines + assert "License-File: LICENSE-XYZ" in license_file_lines + def test_metadata_version(self, tmpdir_cwd, env): """Make sure latest metadata version is used by default.""" self._setup_script_with_requires("") diff --git a/setuptools/tests/test_manifest.py b/setuptools/tests/test_manifest.py index 589cefb2..82bdb9c6 100644 --- a/setuptools/tests/test_manifest.py +++ b/setuptools/tests/test_manifest.py @@ -55,7 +55,6 @@ 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', -- cgit v1.2.1 From b16725ac388b6a0bab6c0ff79d809559589be248 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Thu, 20 May 2021 01:24:01 +0200 Subject: Remove license_files_computed field --- setuptools/command/egg_info.py | 2 +- setuptools/dist.py | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 26ff9a4c..67259c7c 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -593,7 +593,7 @@ class manifest_maker(sdist): self.filelist.graft(ei_cmd.egg_info) def add_license_files(self): - license_files = self.distribution.metadata.license_files_computed + license_files = self.distribution.metadata.license_files or [] for lf in license_files: log.info("adding license file '%s'", lf) pass diff --git a/setuptools/dist.py b/setuptools/dist.py index 9d7fb751..5be83d70 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -142,7 +142,7 @@ def read_pkg_file(self, file): self.provides = None self.obsoletes = None - self.license_files_computed = _read_list_from_msg(msg, 'license-file') + self.license_files = _read_list_from_msg(msg, 'license-file') def single_line(val): @@ -216,7 +216,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME for extra in self.provides_extras: write_field('Provides-Extra', extra) - self._write_list(file, 'License-File', self.license_files_computed) + self._write_list(file, 'License-File', self.license_files or []) file.write("\n%s\n\n" % self.get_long_description()) @@ -421,7 +421,6 @@ class Distribution(_Distribution): 'provides_extras': ordered_set.OrderedSet, 'license_file': None, 'license_files': None, - 'license_files_computed': list, } _patched_dist = None @@ -603,7 +602,7 @@ class Distribution(_Distribution): if path not in files and os.path.isfile(path): files.add(path) - self.metadata.license_files_computed = sorted(files) + self.metadata.license_files = sorted(files) # FIXME: 'Distribution._parse_config_files' is too complex (14) def _parse_config_files(self, filenames=None): # noqa: C901 -- cgit v1.2.1 From 482e8e7687e3272ca2d0b1d5d73b92c3219f4576 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 17 Apr 2021 21:33:23 +0200 Subject: Overwrite exlude from MANIFEST with license_files option * needed for 'License-File' metadata, as this is written before MANIFEST is read --- setuptools/command/egg_info.py | 2 +- setuptools/tests/test_egg_info.py | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'setuptools') diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 67259c7c..18b81340 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -539,9 +539,9 @@ class manifest_maker(sdist): if not os.path.exists(self.manifest): self.write_manifest() # it must exist so it'll get in the list self.add_defaults() - self.add_license_files() if os.path.exists(self.template): self.read_template() + self.add_license_files() self.prune_file_list() self.filelist.sort() self.filelist.remove_duplicates() diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 29fb2062..2821a295 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -551,7 +551,7 @@ class TestEggInfo: """), 'MANIFEST.in': "exclude LICENSE", 'LICENSE': "Test license" - }, False), # license file is manually excluded + }, True), # manifest is overwritten by license_file pytest.param({ 'setup.cfg': DALS(""" [metadata] @@ -647,7 +647,7 @@ class TestEggInfo: """), 'MANIFEST.in': "exclude LICENSE", 'LICENSE': "Test license" - }, [], ['LICENSE']), # license file is manually excluded + }, ['LICENSE'], []), # manifest is overwritten by license_files ({ 'setup.cfg': DALS(""" [metadata] @@ -658,7 +658,8 @@ class TestEggInfo: 'MANIFEST.in': "exclude LICENSE-XYZ", 'LICENSE-ABC': "ABC license", 'LICENSE-XYZ': "XYZ license" - }, ['LICENSE-ABC'], ['LICENSE-XYZ']), # subset is manually excluded + # manifest is overwritten by license_files + }, ['LICENSE-ABC', 'LICENSE-XYZ'], []), pytest.param({ 'setup.cfg': "", 'LICENSE-ABC': "ABC license", @@ -791,8 +792,8 @@ class TestEggInfo: 'LICENSE-ABC': "ABC license", 'LICENSE-PQR': "PQR license", 'LICENSE-XYZ': "XYZ license" - # manually excluded - }, ['LICENSE-XYZ'], ['LICENSE-ABC', 'LICENSE-PQR']), + # manifest is overwritten + }, ['LICENSE-ABC', 'LICENSE-PQR', 'LICENSE-XYZ'], []), pytest.param({ 'setup.cfg': DALS(""" [metadata] -- cgit v1.2.1 From 3d1cce5c6961c2dafc0dc0300c06a0af03e42842 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 22 May 2021 12:23:18 +0200 Subject: Keep user sorting for license files --- setuptools/dist.py | 14 ++++++++------ setuptools/tests/test_egg_info.py | 26 +++++++++++++++++++++----- 2 files changed, 29 insertions(+), 11 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 5be83d70..4877d36b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -581,13 +581,13 @@ class Distribution(_Distribution): def _finalize_license_files(self): """Compute names of all license files which should be included.""" - files = set() + files: List[str] = [] license_files: Optional[List[str]] = self.metadata.license_files - patterns: Set[str] = set(license_files) if license_files else set() + patterns: List[str] = license_files if license_files else [] license_file: Optional[str] = self.metadata.license_file - if license_file: - patterns.add(license_file) + if license_file and license_file not in patterns: + patterns.append(license_file) if license_files is None and license_file is None: # Default patterns match the ones wheel uses @@ -596,13 +596,15 @@ class Distribution(_Distribution): patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') for pattern in patterns: + files_pattern: Set[str] = set() for path in iglob(pattern): if path.endswith('~'): continue if path not in files and os.path.isfile(path): - files.add(path) + files_pattern.add(path) + files.extend(sorted(files_pattern)) - self.metadata.license_files = sorted(files) + self.metadata.license_files = files # FIXME: 'Distribution._parse_config_files' is too complex (14) def _parse_config_files(self, filenames=None): # noqa: C901 diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 2821a295..1d98d259 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -689,6 +689,17 @@ class TestEggInfo: 'NOTICE-XYZ': "XYZ notice", }, ['LICENSE-ABC'], ['NOTICE-XYZ'], id="no_default_glob_patterns"), + pytest.param({ + 'setup.cfg': DALS(""" + [metadata] + license_files = + LICENSE-ABC + LICENSE* + """), + 'LICENSE-ABC': "ABC license", + }, ['LICENSE-ABC'], [], + id="files_only_added_once", + ), ]) def test_setup_cfg_license_files( self, tmpdir_cwd, env, files, incl_licenses, excl_licenses): @@ -843,11 +854,13 @@ class TestEggInfo: "setup.cfg": DALS(""" [metadata] license_files = + NOTICE* LICENSE* """), "LICENSE-ABC": "ABC license", "LICENSE-XYZ": "XYZ license", - "NOTICE": "not included", + "NOTICE": "included", + "IGNORE": "not include", }) environment.run_setup_py( @@ -860,10 +873,13 @@ class TestEggInfo: license_file_lines = [ line for line in pkg_info_lines if line.startswith('License-File:')] - # Only 'LICENSE-ABC' and 'LICENSE-XYZ' should have been matched - assert len(license_file_lines) == 2 - assert "License-File: LICENSE-ABC" in license_file_lines - assert "License-File: LICENSE-XYZ" in license_file_lines + # Only 'NOTICE', LICENSE-ABC', and 'LICENSE-XYZ' should have been matched + # Also assert that order from license_files is keeped + assert license_file_lines == [ + "License-File: NOTICE", + "License-File: LICENSE-ABC", + "License-File: LICENSE-XYZ", + ] def test_metadata_version(self, tmpdir_cwd, env): """Make sure latest metadata version is used by default.""" -- cgit v1.2.1 From e8d7f8861be6acc6f62057604fc2313b16d16b9f Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 22 May 2021 12:32:10 +0200 Subject: Fix after rebase --- setuptools/dist.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 4877d36b..65e6d8b2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -419,8 +419,8 @@ class Distribution(_Distribution): 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, - 'license_file': None, - 'license_files': None, + 'license_file': lambda: None, + 'license_files': lambda: None, } _patched_dist = None -- cgit v1.2.1 From 540a912cce79aa3aed2fcb34c68d4433ad1d096e Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 22 May 2021 13:07:28 +0200 Subject: Remove license_file --- setuptools/dist.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 65e6d8b2..5b901801 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -419,7 +419,6 @@ class Distribution(_Distribution): 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, - 'license_file': lambda: None, 'license_files': lambda: None, } @@ -585,7 +584,10 @@ class Distribution(_Distribution): license_files: Optional[List[str]] = self.metadata.license_files patterns: List[str] = license_files if license_files else [] - license_file: Optional[str] = self.metadata.license_file + license_file: Optional[str] = None + opts = self.get_option_dict('metadata') + if 'license_file' in opts: + license_file = opts['license_file'][1] if license_file and license_file not in patterns: patterns.append(license_file) -- cgit v1.2.1 From c837956cd9f623815df6b2155efac6e13b5e1f7b Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sat, 22 May 2021 13:45:14 +0200 Subject: Revert removal of license_file --- setuptools/dist.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 5b901801..65e6d8b2 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -419,6 +419,7 @@ class Distribution(_Distribution): 'long_description_content_type': lambda: None, 'project_urls': dict, 'provides_extras': ordered_set.OrderedSet, + 'license_file': lambda: None, 'license_files': lambda: None, } @@ -584,10 +585,7 @@ class Distribution(_Distribution): license_files: Optional[List[str]] = self.metadata.license_files patterns: List[str] = license_files if license_files else [] - license_file: Optional[str] = None - opts = self.get_option_dict('metadata') - if 'license_file' in opts: - license_file = opts['license_file'][1] + license_file: Optional[str] = self.metadata.license_file if license_file and license_file not in patterns: patterns.append(license_file) -- cgit v1.2.1 From 4429ffbb700c1838ddf57c47c378bd3c617eb8c3 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sat, 22 May 2021 18:54:37 -0400 Subject: Replace for/if/add/extend with generator on patterns. Use unique_everseen to dedupe. --- setuptools/dist.py | 41 ++++++++++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 11 deletions(-) (limited to 'setuptools') diff --git a/setuptools/dist.py b/setuptools/dist.py index 65e6d8b2..067d7f5b 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -18,7 +18,7 @@ from distutils.fancy_getopt import translate_longopt from glob import iglob import itertools import textwrap -from typing import List, Optional, Set, TYPE_CHECKING +from typing import List, Optional, TYPE_CHECKING from collections import defaultdict from email import message_from_file @@ -581,7 +581,6 @@ class Distribution(_Distribution): def _finalize_license_files(self): """Compute names of all license files which should be included.""" - files: List[str] = [] license_files: Optional[List[str]] = self.metadata.license_files patterns: List[str] = license_files if license_files else [] @@ -595,16 +594,18 @@ class Distribution(_Distribution): # -> 'Including license files in the generated wheel file' patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') - for pattern in patterns: - files_pattern: Set[str] = set() - for path in iglob(pattern): - if path.endswith('~'): - continue - if path not in files and os.path.isfile(path): - files_pattern.add(path) - files.extend(sorted(files_pattern)) + self.metadata.license_files = list( + unique_everseen(self._expand_patterns(patterns))) - self.metadata.license_files = files + @staticmethod + def _expand_patterns(patterns): + return ( + path + for pattern in patterns + for path in iglob(pattern) + if not path.endswith('~') + and os.path.isfile(path) + ) # FIXME: 'Distribution._parse_config_files' is too complex (14) def _parse_config_files(self, filenames=None): # noqa: C901 @@ -1111,3 +1112,21 @@ class Distribution(_Distribution): class DistDeprecationWarning(SetuptoolsDeprecationWarning): """Class for warning about deprecations in dist in setuptools. Not ignored by default, unlike DeprecationWarning.""" + + +def unique_everseen(iterable, key=None): + "List unique elements, preserving order. Remember all elements ever seen." + # unique_everseen('AAAABBBCCDAABBB') --> A B C D + # unique_everseen('ABBCcAD', str.lower) --> A B C D + seen = set() + seen_add = seen.add + if key is None: + for element in itertools.filterfalse(seen.__contains__, iterable): + seen_add(element) + yield element + else: + for element in iterable: + k = key(element) + if k not in seen: + seen_add(k) + yield element -- cgit v1.2.1 From acf4807fd651cd94ee646f881733af796b0e30d3 Mon Sep 17 00:00:00 2001 From: Marc Mueller <30130371+cdce8p@users.noreply.github.com> Date: Sun, 23 May 2021 01:51:09 +0200 Subject: Fix flaky test --- setuptools/tests/test_egg_info.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) (limited to 'setuptools') diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 1d98d259..ee07b5a1 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -875,11 +875,9 @@ class TestEggInfo: # Only 'NOTICE', LICENSE-ABC', and 'LICENSE-XYZ' should have been matched # Also assert that order from license_files is keeped - assert license_file_lines == [ - "License-File: NOTICE", - "License-File: LICENSE-ABC", - "License-File: LICENSE-XYZ", - ] + assert "License-File: NOTICE" == license_file_lines[0] + assert "License-File: LICENSE-ABC" in license_file_lines[1:] + assert "License-File: LICENSE-XYZ" in license_file_lines[1:] def test_metadata_version(self, tmpdir_cwd, env): """Make sure latest metadata version is used by default.""" -- cgit v1.2.1