diff options
| author | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2022-04-04 10:09:08 +0100 |
|---|---|---|
| committer | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2022-04-04 10:09:08 +0100 |
| commit | 3465caca4fb3b9fe2c41c378cb693655a87c1f33 (patch) | |
| tree | 4807a17b77361f561014a00ed2f797ca9c88a897 | |
| parent | cde42d6303f170eafd4e925665701e88a4d65e0d (diff) | |
| parent | b1dca400264fb4c1471d4040fd63d5c76ed38a83 (diff) | |
| download | python-setuptools-git-3465caca4fb3b9fe2c41c378cb693655a87c1f33.tar.gz | |
Fix version produced by dist_info (#3230)
| -rw-r--r-- | changelog.d/3088.misc.rst | 1 | ||||
| -rw-r--r-- | setuptools/command/dist_info.py | 34 | ||||
| -rw-r--r-- | setuptools/command/egg_info.py | 14 | ||||
| -rw-r--r-- | setuptools/tests/test_dist_info.py | 84 |
4 files changed, 130 insertions, 3 deletions
diff --git a/changelog.d/3088.misc.rst b/changelog.d/3088.misc.rst new file mode 100644 index 00000000..c507a824 --- /dev/null +++ b/changelog.d/3088.misc.rst @@ -0,0 +1 @@ +Fixed duplicated tag with the ``dist-info`` command. diff --git a/setuptools/command/dist_info.py b/setuptools/command/dist_info.py index c45258fa..8b8509f3 100644 --- a/setuptools/command/dist_info.py +++ b/setuptools/command/dist_info.py @@ -4,9 +4,13 @@ As defined in the wheel specification """ import os +import re +import warnings +from inspect import cleandoc from distutils.core import Command from distutils import log +from setuptools.extern import packaging class dist_info(Command): @@ -29,8 +33,36 @@ class dist_info(Command): egg_info.egg_base = self.egg_base egg_info.finalize_options() egg_info.run() - dist_info_dir = egg_info.egg_info[:-len('.egg-info')] + '.dist-info' + name = _safe(self.distribution.get_name()) + version = _version(self.distribution.get_version()) + base = self.egg_base or os.curdir + dist_info_dir = os.path.join(base, f"{name}-{version}.dist-info") log.info("creating '{}'".format(os.path.abspath(dist_info_dir))) bdist_wheel = self.get_finalized_command('bdist_wheel') bdist_wheel.egg2dist(egg_info.egg_info, dist_info_dir) + + +def _safe(component: str) -> str: + """Escape a component used to form a wheel name according to PEP 491""" + return re.sub(r"[^\w\d.]+", "_", component) + + +def _version(version: str) -> str: + """Convert an arbitrary string to a version string.""" + v = version.replace(' ', '.') + try: + return str(packaging.version.Version(v)).replace("-", "_") + except packaging.version.InvalidVersion: + msg = f"""!!\n\n + ################### + # Invalid version # + ################### + {version!r} is not valid according to PEP 440.\n + Please make sure specify a valid version for your package. + Also note that future releases of setuptools may halt the build process + if an invalid version is given. + \n\n!! + """ + warnings.warn(cleandoc(msg)) + return _safe(v).strip("_") diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index 63389654..c37ab81f 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -136,11 +136,21 @@ class InfoCommon: in which case the version string already contains all tags. """ return ( - version if self.vtags and version.endswith(self.vtags) + version if self.vtags and self._already_tagged(version) else version + self.vtags ) - def tags(self): + def _already_tagged(self, version: str) -> bool: + # Depending on their format, tags may change with version normalization. + # So in addition the regular tags, we have to search for the normalized ones. + return version.endswith(self.vtags) or version.endswith(self._safe_tags()) + + def _safe_tags(self) -> str: + # To implement this we can rely on `safe_version` pretending to be version 0 + # followed by tags. Then we simply discard the starting 0 (fake version number) + return safe_version(f"0{self.vtags}")[1:] + + def tags(self) -> str: version = '' if self.tag_build: version += self.tag_build diff --git a/setuptools/tests/test_dist_info.py b/setuptools/tests/test_dist_info.py index 29fbd09d..813ef51d 100644 --- a/setuptools/tests/test_dist_info.py +++ b/setuptools/tests/test_dist_info.py @@ -1,12 +1,21 @@ """Test .dist-info style distributions. """ +import pathlib +import re +import subprocess +import sys +from functools import partial import pytest import pkg_resources +from setuptools.archive_util import unpack_archive from .textwrap import DALS +read = partial(pathlib.Path.read_text, encoding="utf-8") + + class TestDistInfo: metadata_base = DALS(""" @@ -72,3 +81,78 @@ class TestDistInfo: pkg_resources.Requirement.parse('quux>=1.1;extra=="baz"'), ] assert d.extras == ['baz'] + + def test_invalid_version(self, tmp_path): + config = "[metadata]\nname=proj\nversion=42\n[egg_info]\ntag_build=invalid!!!\n" + (tmp_path / "setup.cfg").write_text(config, encoding="utf-8") + msg = re.compile("invalid version", re.M | re.I) + output = run_command("dist_info", cwd=tmp_path) + assert msg.search(output) + dist_info = next(tmp_path.glob("*.dist-info")) + assert dist_info.name.startswith("proj-42") + + +class TestWheelCompatibility: + """Make sure the .dist-info directory produced with the ``dist_info`` command + is the same as the one produced by ``bdist_wheel``. + """ + SETUPCFG = DALS(""" + [metadata] + name = {name} + version = {version} + + [options] + install_requires = foo>=12; sys_platform != "linux" + + [options.extras_require] + test = pytest + + [options.entry_points] + console_scripts = + executable-name = my_package.module:function + discover = + myproj = my_package.other_module:function + """) + + EGG_INFO_OPTS = [ + # Related: #3088 #2872 + ("", ""), + (".post", "[egg_info]\ntag_build = post\n"), + (".post", "[egg_info]\ntag_build = .post\n"), + (".post", "[egg_info]\ntag_build = post\ntag_date = 1\n"), + (".dev", "[egg_info]\ntag_build = .dev\n"), + (".dev", "[egg_info]\ntag_build = .dev\ntag_date = 1\n"), + ("a1", "[egg_info]\ntag_build = .a1\n"), + ("+local", "[egg_info]\ntag_build = +local\n"), + ] + + @pytest.mark.parametrize("name", "my-proj my_proj my.proj My.Proj".split()) + @pytest.mark.parametrize("version", ["0.42.13"]) + @pytest.mark.parametrize("suffix, cfg", EGG_INFO_OPTS) + def test_dist_info_is_the_same_as_in_wheel( + self, name, version, tmp_path, suffix, cfg + ): + config = self.SETUPCFG.format(name=name, version=version) + cfg + + for i in "dir_wheel", "dir_dist": + (tmp_path / i).mkdir() + (tmp_path / i / "setup.cfg").write_text(config, encoding="utf-8") + + run_command("bdist_wheel", cwd=tmp_path / "dir_wheel") + wheel = next(tmp_path.glob("dir_wheel/dist/*.whl")) + unpack_archive(wheel, tmp_path / "unpack") + wheel_dist_info = next(tmp_path.glob("unpack/*.dist-info")) + + run_command("dist_info", cwd=tmp_path / "dir_dist") + dist_info = next(tmp_path.glob("dir_dist/*.dist-info")) + + assert dist_info.name == wheel_dist_info.name + assert dist_info.name.startswith(f"{name.replace('-', '_')}-{version}{suffix}") + for file in "METADATA", "entry_points.txt": + assert read(dist_info / file) == read(wheel_dist_info / file) + + +def run_command(*cmd, **kwargs): + opts = {"stderr": subprocess.STDOUT, "text": True, **kwargs} + cmd = [sys.executable, "-c", "__import__('setuptools').setup()", *cmd] + return subprocess.check_output(cmd, **opts) |
