diff --git a/metadata_please/sdist.py b/metadata_please/sdist.py index 7194c1c..0c50f5a 100644 --- a/metadata_please/sdist.py +++ b/metadata_please/sdist.py @@ -30,12 +30,23 @@ def from_zip_sdist(zf: ZipFile) -> bytes: def basic_metadata_from_zip_sdist(zf: ZipFile) -> BasicMetadata: requires = [f for f in zf.namelist() if f.endswith("/requires.txt")] requires.sort(key=len) - if not requires: - return BasicMetadata((), frozenset(), "-") + if requires: + requires_data = zf.read(requires[0]) + assert requires_data is not None + else: + requires_data = b"" - data = zf.read(requires[0]) - assert data is not None - return BasicMetadata.from_sdist_pkg_info_and_requires(b"", data) + # Find the PKG-INFO file with the shortest path. This is to avoid picking up + # a PKG-INFO file from a nested test directory. + pkg_info = sorted( + (f for f in zf.namelist() if f == "PKG-INFO" or f.endswith("/PKG-INFO")), + key=len, + )[0] + + pkg_info_data = zf.read(pkg_info) + assert pkg_info_data is not None + + return BasicMetadata.from_sdist_pkg_info_and_requires(pkg_info_data, requires_data) def from_tar_sdist(tf: TarFile) -> bytes: @@ -58,6 +69,7 @@ def from_tar_sdist(tf: TarFile) -> bytes: buf.append(f"Requires-Dist: {req}\n") for extra in sorted(extras): buf.append(f"Provides-Extra: {extra}\n") + return ("".join(buf)).encode("utf-8") @@ -65,10 +77,23 @@ def basic_metadata_from_tar_sdist(tf: TarFile) -> BasicMetadata: # XXX Why do ZipFile and TarFile not have a common interface ?! requires = [f for f in tf.getnames() if f.endswith("/requires.txt")] requires.sort(key=len) - if not requires: - return BasicMetadata((), frozenset()) - - fo = tf.extractfile(requires[0]) - assert fo is not None - - return BasicMetadata.from_sdist_pkg_info_and_requires(b"", fo.read()) + if requires: + requires_fo = tf.extractfile(requires[0]) + assert requires_fo is not None + requires_data = requires_fo.read() + else: + requires_data = b"" + + # Find the PKG-INFO file with the shortest path. This is to avoid picking up + # a PKG-INFO file from a nested test directory. + pkg_info = sorted( + (f for f in tf.getnames() if f == "PKG-INFO" or f.endswith("/PKG-INFO")), + key=len, + )[0] + + pkg_info_fo = tf.extractfile(pkg_info) + assert pkg_info_fo is not None + + return BasicMetadata.from_sdist_pkg_info_and_requires( + pkg_info_fo.read(), requires_data + ) diff --git a/metadata_please/tests/metadata_contents.py b/metadata_please/tests/metadata_contents.py new file mode 100644 index 0000000..e59e228 --- /dev/null +++ b/metadata_please/tests/metadata_contents.py @@ -0,0 +1,15 @@ +METADATA_CONTENTS = b"""\ +Requires-Dist: foo +Version: 1.2.58 +Summary: Some Summary +Home-page: http://example.com +Author: Chicken +Author-email: duck@example.com +Keywords: farm,animals +Requires-Python: >=3.6 +Description-Content-Type: text/markdown + +# Foo + +A very important package. +""" diff --git a/metadata_please/tests/sdist.py b/metadata_please/tests/sdist.py index b222c95..9bdbc6d 100644 --- a/metadata_please/tests/sdist.py +++ b/metadata_please/tests/sdist.py @@ -8,6 +8,7 @@ ) from ._tar import MemoryTarFile from ._zip import MemoryZipFile +from .metadata_contents import METADATA_CONTENTS class ZipSdistTest(unittest.TestCase): @@ -15,6 +16,7 @@ def test_requires_as_expected(self) -> None: z = MemoryZipFile( { "foo/__init__.py": b"", + "foo.egg-info/PKG-INFO": b"\n", "foo.egg-info/requires.txt": b"""\ a [e] @@ -36,6 +38,7 @@ def test_basic_metadata(self) -> None: z = MemoryZipFile( { "foo/__init__.py": b"", + "foo.egg-info/PKG-INFO": b"\n", "foo.egg-info/requires.txt": b"""\ a [e] @@ -68,6 +71,7 @@ def test_basic_metadata_absl_py_09(self) -> None: z = MemoryZipFile( { "foo/__init__.py": b"", + "foo.egg-info/PKG-INFO": b"\n", "foo.egg-info/requires.txt": b"""\ six @@ -90,11 +94,32 @@ def test_basic_metadata_absl_py_09(self) -> None: ) self.assertEqual({"test"}, bm.provides_extra) + def test_basic_metadata_fields(self) -> None: + """ + Modern setuptools will drop a PKG-INFO file in a sdist that is very similar to the METADATA file in a wheel. + """ + z = MemoryZipFile( + { + "foo/__init__.py": b"", + "PKG-INFO": METADATA_CONTENTS, + } + ) + bm = basic_metadata_from_zip_sdist(z) # type: ignore + self.assertEqual(["foo"], bm.reqs) + self.assertEqual("1.2.58", bm.version) + self.assertEqual("Some Summary", bm.summary) + self.assertEqual("http://example.com", bm.url) + self.assertEqual("Chicken", bm.author) + self.assertEqual("duck@example.com", bm.author_email) + self.assertEqual("farm,animals", bm.keywords) + self.assertEqual("text/markdown", bm.long_description_content_type) + self.assertEqual("# Foo\n\nA very important package.\n", bm.description) + class TarSdistTest(unittest.TestCase): def test_requires_as_expected(self) -> None: t = MemoryTarFile( - ["foo.egg-info/requires.txt", "foo/__init__.py"], + ["foo.egg-info/PKG-INFO", "foo.egg-info/requires.txt", "foo/__init__.py"], read_value=b"""\ a [e] @@ -113,7 +138,7 @@ def test_requires_as_expected(self) -> None: def test_basic_metadata(self) -> None: t = MemoryTarFile( - ["foo.egg-info/requires.txt", "foo/__init__.py"], + ["foo.egg-info/PKG-INFO", "foo.egg-info/requires.txt", "foo/__init__.py"], read_value=b"""\ a [e] @@ -126,3 +151,18 @@ def test_basic_metadata(self) -> None: bm.reqs, ) self.assertEqual({"e"}, bm.provides_extra) + + def test_metadata_fields_from_tar_sdist(self) -> None: + t = MemoryTarFile( + ["PKG-INFO", "foo/__init__.py"], + read_value=METADATA_CONTENTS, + ) + bm = basic_metadata_from_tar_sdist(t) # type: ignore + self.assertEqual("1.2.58", bm.version) + self.assertEqual("Some Summary", bm.summary) + self.assertEqual("http://example.com", bm.url) + self.assertEqual("Chicken", bm.author) + self.assertEqual("duck@example.com", bm.author_email) + self.assertEqual("farm,animals", bm.keywords) + self.assertEqual("text/markdown", bm.long_description_content_type) + self.assertEqual("# Foo\n\nA very important package.\n", bm.description) diff --git a/metadata_please/tests/wheel.py b/metadata_please/tests/wheel.py index 7343202..33ec67e 100644 --- a/metadata_please/tests/wheel.py +++ b/metadata_please/tests/wheel.py @@ -2,6 +2,7 @@ from ..wheel import basic_metadata_from_wheel, from_wheel, InvalidWheel from ._zip import MemoryZipFile +from .metadata_contents import METADATA_CONTENTS class WheelTest(unittest.TestCase): @@ -58,7 +59,7 @@ def test_basic_metadata(self) -> None: def test_basic_metadata_more_fields(self) -> None: z = MemoryZipFile( { - "foo.dist-info/METADATA": b"Requires-Dist: foo\nVersion: 1.2.58\nSummary: Some Summary\nHome-page: http://example.com\nAuthor: Chicken\nAuthor-email: duck@example.com\nKeywords: farm,animals\nRequires-Python: >=3.6\nDescription-Content-Type: text/markdown", + "foo.dist-info/METADATA": METADATA_CONTENTS, "foo/__init__.py": b"", } ) @@ -71,4 +72,4 @@ def test_basic_metadata_more_fields(self) -> None: self.assertEqual("duck@example.com", bm.author_email) self.assertEqual("farm,animals", bm.keywords) self.assertEqual("text/markdown", bm.long_description_content_type) - self.assertEqual(None, bm.description) + self.assertEqual("# Foo\n\nA very important package.\n", bm.description)