From f175ccd6ffdf70a9314fbe2431aff84d3fe82460 Mon Sep 17 00:00:00 2001 From: Tim Hatch Date: Tue, 27 Feb 2024 07:23:15 -0800 Subject: [PATCH 1/3] Improve BasicMetadata to be more immutable --- metadata_please/sdist.py | 8 ++++---- metadata_please/tests/sdist.py | 8 ++++---- metadata_please/types.py | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/metadata_please/sdist.py b/metadata_please/sdist.py index c2bd5c2..c054fd6 100644 --- a/metadata_please/sdist.py +++ b/metadata_please/sdist.py @@ -14,10 +14,10 @@ def from_zip_sdist(zf: ZipFile) -> bytes: requires.sort(key=len) data = zf.read(requires[0]) assert data is not None - requires, extras = convert_sdist_requires(data.decode("utf-8")) + requires_lines, extras = convert_sdist_requires(data.decode("utf-8")) buf: list[str] = [] - for req in requires: + for req in requires_lines: buf.append(f"Requires-Dist: {req}\n") for extra in sorted(extras): buf.append(f"Provides-Extra: {extra}\n") @@ -43,10 +43,10 @@ def from_tar_sdist(tf: TarFile) -> bytes: fo = tf.extractfile(requires[0]) assert fo is not None - requires, extras = convert_sdist_requires(fo.read().decode("utf-8")) + requires_lines, extras = convert_sdist_requires(fo.read().decode("utf-8")) buf: list[str] = [] - for req in requires: + for req in requires_lines: buf.append(f"Requires-Dist: {req}\n") for extra in sorted(extras): buf.append(f"Provides-Extra: {extra}\n") diff --git a/metadata_please/tests/sdist.py b/metadata_please/tests/sdist.py index e0ecd93..b67a979 100644 --- a/metadata_please/tests/sdist.py +++ b/metadata_please/tests/sdist.py @@ -41,7 +41,7 @@ def test_basic_metadata(self) -> None: ) bm = basic_metadata_from_zip_sdist(z) # type: ignore self.assertEqual( - ["a", "b; extra == 'e'"], + ("a", "b; extra == 'e'"), bm.reqs, ) self.assertEqual({"e"}, bm.provides_extra) @@ -60,12 +60,12 @@ def test_basic_metadata_absl_py_09(self) -> None: ) bm = basic_metadata_from_zip_sdist(z) # type: ignore self.assertEqual( - [ + ( "six", 'enum34; python_version < "3.4"', # Quoting on the following line is an implementation detail "pytest; (python_version < \"3.4\") and extra == 'test'", - ], + ), bm.reqs, ) self.assertEqual({"test"}, bm.provides_extra) @@ -102,7 +102,7 @@ def test_basic_metadata(self) -> None: ) bm = basic_metadata_from_tar_sdist(t) # type: ignore self.assertEqual( - ["a", "b; extra == 'e'"], + ("a", "b; extra == 'e'"), bm.reqs, ) self.assertEqual({"e"}, bm.provides_extra) diff --git a/metadata_please/types.py b/metadata_please/types.py index 12a0fcf..368f83e 100644 --- a/metadata_please/types.py +++ b/metadata_please/types.py @@ -11,14 +11,14 @@ class BasicMetadata: # Popualted from Requires-Dist or requires.txt reqs: Sequence[str] # Populated from Provides-Extra - provides_extra: set[str] + provides_extra: frozenset[str] @classmethod def from_metadata(cls, metadata: bytes) -> BasicMetadata: msg = message_from_string(metadata.decode("utf-8")) return BasicMetadata( msg.get_all("Requires-Dist") or (), - set(msg.get_all("Provides-Extra") or ()), + frozenset(msg.get_all("Provides-Extra") or ()), ) @classmethod @@ -33,7 +33,7 @@ def from_sdist_pkg_info_and_requires( ) -def convert_sdist_requires(data: str) -> tuple[list[str], set[str]]: +def convert_sdist_requires(data: str) -> tuple[tuple[str, ...], frozenset[str]]: # This is reverse engineered from looking at a couple examples, but there # does not appear to be a formal spec. Mentioned at # https://setuptools.readthedocs.io/en/latest/formats.html#requires-txt @@ -64,4 +64,4 @@ def convert_sdist_requires(data: str) -> tuple[list[str], set[str]]: lst.append(f"{line}; {current_markers}") else: lst.append(line) - return lst, extras + return tuple(lst), frozenset(extras) From 5cc90a7d700b394bb3147205a5dd53357a22b3b2 Mon Sep 17 00:00:00 2001 From: Tim Hatch Date: Tue, 27 Feb 2024 07:22:20 -0800 Subject: [PATCH 2/3] Handle sdists that do not have any requirements grpc-tools (which fails to build a wheel with a message saying to install grpcio-tools) is such a project --- metadata_please/sdist.py | 10 ++++++++++ metadata_please/tests/sdist.py | 12 ++++++++++++ 2 files changed, 22 insertions(+) diff --git a/metadata_please/sdist.py b/metadata_please/sdist.py index c054fd6..743d673 100644 --- a/metadata_please/sdist.py +++ b/metadata_please/sdist.py @@ -12,6 +12,9 @@ def from_zip_sdist(zf: ZipFile) -> bytes: """ requires = [f for f in zf.namelist() if f.endswith("/requires.txt")] requires.sort(key=len) + if not requires: + return b"" + data = zf.read(requires[0]) assert data is not None requires_lines, extras = convert_sdist_requires(data.decode("utf-8")) @@ -27,6 +30,9 @@ 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()) + data = zf.read(requires[0]) assert data is not None return BasicMetadata.from_sdist_pkg_info_and_requires(b"", data) @@ -39,6 +45,8 @@ def from_tar_sdist(tf: TarFile) -> bytes: # 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 b"" fo = tf.extractfile(requires[0]) assert fo is not None @@ -57,6 +65,8 @@ 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 diff --git a/metadata_please/tests/sdist.py b/metadata_please/tests/sdist.py index b67a979..529b29e 100644 --- a/metadata_please/tests/sdist.py +++ b/metadata_please/tests/sdist.py @@ -46,6 +46,18 @@ def test_basic_metadata(self) -> None: ) self.assertEqual({"e"}, bm.provides_extra) + def test_basic_metadata_no_requires_file(self) -> None: + z = MemoryZipFile( + ["foo.egg-info/PKG-INFO", "foo/__init__.py"], + read_value=b"\n", + ) + bm = basic_metadata_from_zip_sdist(z) # type: ignore + self.assertEqual( + (), + bm.reqs, + ) + self.assertEqual(set(), bm.provides_extra) + def test_basic_metadata_absl_py_09(self) -> None: z = MemoryZipFile( ["foo.egg-info/requires.txt", "foo/__init__.py"], From 1940706b0df1a2af94f0c9f133565bc56ba0b133 Mon Sep 17 00:00:00 2001 From: Tim Hatch Date: Tue, 27 Feb 2024 07:29:48 -0800 Subject: [PATCH 3/3] Fix just-tests workflow to not require full dev deps --- .github/workflows/build.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 33b64d7..c89a46b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,12 +29,9 @@ jobs: - name: Install run: | python -m pip install --upgrade pip - make setup pip install -U . - name: Test - run: make test - - name: Lint - run: make lint + run: python -m metadata_please.tests metadata_please: runs-on: ${{ matrix.os }}