diff options
| -rw-r--r-- | .bumpversion.cfg | 2 | ||||
| -rw-r--r-- | CHANGES.rst | 13 | ||||
| -rw-r--r-- | docs/userguide/quickstart.rst | 9 | ||||
| -rw-r--r-- | setup.cfg | 2 | ||||
| -rw-r--r-- | setuptools/dist.py | 53 | ||||
| -rw-r--r-- | setuptools/tests/test_dist.py | 10 | ||||
| -rw-r--r-- | setuptools/tests/test_egg_info.py | 57 | ||||
| -rw-r--r-- | tools/finalize.py | 12 |
8 files changed, 106 insertions, 52 deletions
diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 7b7bed92..036117a5 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 56.1.0 +current_version = 56.2.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 13e936e0..f7dd6a75 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,4 +1,15 @@ -v56.1.0v56.1.0 +v56.2.0 +------- + + +Changes +^^^^^^^ +* #2640: Fixed handling of multiline license strings. - by :user:`cdce8p` +* #2641: Setuptools will now always try to use the latest supported + metadata version for ``PKG-INFO``. - by :user:`cdce8p` + + +v56.1.0 ------- diff --git a/docs/userguide/quickstart.rst b/docs/userguide/quickstart.rst index 2807f59b..a9f0bbae 100644 --- a/docs/userguide/quickstart.rst +++ b/docs/userguide/quickstart.rst @@ -22,7 +22,7 @@ be generated with whatever tools that provides a ``build sdist``-alike functionality. While this may appear cumbersome, given the added pieces, it in fact tremendously enhances the portability of your package. The change is driven under :pep:`PEP 517 <517#build-requirements>`. To learn more about Python packaging in general, -navigate to the `bottom <Resources on python packaging>`_ of this page. +navigate to the :ref:`bottom <packaging-resources>` of this page. Basic Use @@ -52,8 +52,8 @@ the minimum [options] packages = mypackage install_requires = - requests - importlib; python_version == "2.6" + requests + importlib; python_version == "2.6" .. tab:: setup.py @@ -215,13 +215,14 @@ basic use here. Transitioning from ``setup.py`` to ``setup.cfg`` -================================================== +================================================ To avoid executing arbitary scripts and boilerplate code, we are transitioning into a full-fledged ``setup.cfg`` to declare your package information instead of running ``setup()``. This inevitably brings challenges due to a different syntax. Here we provide a quick guide to understanding how ``setup.cfg`` is parsed by ``setuptool`` to ease the pain of transition. +.. _packaging-resources: Resources on Python packaging ============================= @@ -2,7 +2,7 @@ license_files = LICENSE name = setuptools -version = 56.1.0 +version = 56.2.0 author = Python Packaging Authority author_email = distutils-sig@python.org description = Easily download, build, install, upgrade, and uninstall Python packages diff --git a/setuptools/dist.py b/setuptools/dist.py index c7af35dc..24aef1bb 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -52,23 +52,9 @@ def _get_unpatched(cls): def get_metadata_version(self): mv = getattr(self, 'metadata_version', None) - if mv is None: - if self.long_description_content_type or self.provides_extras: - mv = StrictVersion('2.1') - elif (self.maintainer is not None or - self.maintainer_email is not None or - getattr(self, 'python_requires', None) is not None or - self.project_urls): - mv = StrictVersion('1.2') - elif (self.provides or self.requires or self.obsoletes or - self.classifiers or self.download_url): - mv = StrictVersion('1.1') - else: - mv = StrictVersion('1.0') - + mv = StrictVersion('2.1') self.metadata_version = mv - return mv @@ -120,7 +106,7 @@ def read_pkg_file(self, file): self.author_email = _read_field_from_msg(msg, 'author-email') self.maintainer_email = None self.url = _read_field_from_msg(msg, 'home-page') - self.license = _read_field_from_msg(msg, 'license') + self.license = _read_field_unescaped_from_msg(msg, 'license') if 'download-url' in msg: self.download_url = _read_field_from_msg(msg, 'download-url') @@ -171,24 +157,20 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME write_field('Summary', single_line(self.get_description())) write_field('Home-page', self.get_url()) - if version < StrictVersion('1.2'): - write_field('Author', self.get_contact()) - write_field('Author-email', self.get_contact_email()) - else: - optional_fields = ( - ('Author', 'author'), - ('Author-email', 'author_email'), - ('Maintainer', 'maintainer'), - ('Maintainer-email', 'maintainer_email'), - ) + optional_fields = ( + ('Author', 'author'), + ('Author-email', 'author_email'), + ('Maintainer', 'maintainer'), + ('Maintainer-email', 'maintainer_email'), + ) - for field, attr in optional_fields: - attr_val = getattr(self, attr) + for field, attr in optional_fields: + attr_val = getattr(self, attr, None) + if attr_val is not None: + write_field(field, attr_val) - if attr_val is not None: - write_field(field, attr_val) - - write_field('License', self.get_license()) + license = rfc822_escape(self.get_license()) + write_field('License', license) if self.download_url: write_field('Download-URL', self.download_url) for project_url in self.project_urls.items(): @@ -201,11 +183,8 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME if keywords: write_field('Keywords', keywords) - if version >= StrictVersion('1.2'): - for platform in self.get_platforms(): - write_field('Platform', platform) - else: - self._write_list(file, 'Platform', self.get_platforms()) + for platform in self.get_platforms(): + write_field('Platform', platform) self._write_list(file, 'Classifier', self.get_classifiers()) diff --git a/setuptools/tests/test_dist.py b/setuptools/tests/test_dist.py index dcec1734..6378caef 100644 --- a/setuptools/tests/test_dist.py +++ b/setuptools/tests/test_dist.py @@ -84,15 +84,9 @@ def __read_test_cases(): test_cases = [ ('Metadata version 1.0', params()), - ('Metadata version 1.1: Provides', params( - provides=['package'], - )), ('Metadata Version 1.0: Short long description', params( long_description='Short long description', )), - ('Metadata version 1.1: Obsoletes', params( - obsoletes=['foo'], - )), ('Metadata version 1.1: Classifiers', params( classifiers=[ 'Programming Language :: Python :: 3', @@ -116,6 +110,10 @@ def __read_test_cases(): ('Metadata Version 2.1: Long Description Content Type', params( long_description_content_type='text/x-rst; charset=UTF-8', )), + ('License', params(license='MIT', )), + ('License multiline', params( + license='This is a long license \nover multiple lines', + )), pytest.param( 'Metadata Version 2.1: Provides Extra', params(provides_extras=['foo', 'bar']), diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index 80d35774..0d595ad0 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -5,6 +5,7 @@ import glob import re import stat import time +from typing import List, Tuple import pytest from jaraco import path @@ -45,6 +46,11 @@ class TestEggInfo: """) }) + @staticmethod + def _extract_mv_version(pkg_info_lines: List[str]) -> Tuple[int, int]: + version_str = pkg_info_lines[0].split(' ')[1] + return tuple(map(int, version_str.split('.')[:2])) + @pytest.fixture def env(self): with contexts.tempdir(prefix='setuptools-test.') as env_dir: @@ -829,6 +835,20 @@ class TestEggInfo: for lf in excl_licenses: assert sources_lines.count(lf) == 0 + def test_metadata_version(self, tmpdir_cwd, env): + """Make sure latest metadata version is used by default.""" + self._setup_script_with_requires("") + code, data = environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + 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') + # Update metadata version if changed + assert self._extract_mv_version(pkg_info_lines) == (2, 1) + def test_long_description_content_type(self, tmpdir_cwd, env): # Test that specifying a `long_description_content_type` keyword arg to # the `setup` function results in writing a `Description-Content-Type` @@ -884,7 +904,40 @@ class TestEggInfo: assert expected_line in pkg_info_lines expected_line = 'Project-URL: Link Two, https://example.com/two/' assert expected_line in pkg_info_lines - assert 'Metadata-Version: 1.2' in pkg_info_lines + assert self._extract_mv_version(pkg_info_lines) >= (1, 2) + + def test_license(self, tmpdir_cwd, env): + """Test single line license.""" + self._setup_script_with_requires( + "license='MIT'," + ) + code, data = environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + 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 'License: MIT' in pkg_info_lines + + def test_license_escape(self, tmpdir_cwd, env): + """Test license is escaped correctly if longer than one line.""" + self._setup_script_with_requires( + "license='This is a long license text \\nover multiple lines'," + ) + code, data = environment.run_setup_py( + cmd=['egg_info'], + pypath=os.pathsep.join([env.paths['lib'], str(tmpdir_cwd)]), + data_stream=1, + ) + 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 'License: This is a long license text ' in pkg_info_lines + assert ' over multiple lines' in pkg_info_lines + assert 'text \n over multiple' in '\n'.join(pkg_info_lines) def test_python_requires_egg_info(self, tmpdir_cwd, env): self._setup_script_with_requires( @@ -902,7 +955,7 @@ class TestEggInfo: with open(os.path.join(egg_info_dir, 'PKG-INFO')) as pkginfo_file: pkg_info_lines = pkginfo_file.read().split('\n') assert 'Requires-Python: >=2.7.12' in pkg_info_lines - assert 'Metadata-Version: 1.2' in pkg_info_lines + assert self._extract_mv_version(pkg_info_lines) >= (1, 2) def test_manifest_maker_warning_suppression(self): fixtures = [ diff --git a/tools/finalize.py b/tools/finalize.py index 35294281..516a2fb5 100644 --- a/tools/finalize.py +++ b/tools/finalize.py @@ -46,6 +46,18 @@ def update_changelog(): '--yes', ] subprocess.check_call(cmd) + _repair_changelog() + + +def _repair_changelog(): + """ + Workaround for #2666 + """ + changelog_fn = pathlib.Path('CHANGES.rst') + changelog = changelog_fn.read_text() + fixed = re.sub(r'^(v[0-9.]+)v[0-9.]+$', r'\1', changelog, flags=re.M) + changelog_fn.write_text(fixed) + subprocess.check_output(['git', 'add', changelog_fn]) def bump_version(): |
